Railway Operation Simulator  v2.13.2
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand() & random()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 //#include "DisplayUnit.h" included in TrackUnit.h
49 #include "PerfLogUnit.h"
50 #include "Utilities.h"
51 
52 // ---------------------------------------------------------------------------
53 #pragma package(smart_init)
54 
56 
57 // ---------------------------------------------------------------------------
58 
59 int TTrain::NextTrainID = 0; // has to be initialised outside the class
60 
61 // ---------------------------------------------------------------------------
62 
63 TExitInfo::TExitInfo() //default constructor
64 {
65  ServiceReference = " ";
66  RepeatNumber = 0;
67  TimeToExitSecs = -1;
68 }
69 
70 // ---------------------------------------------------------------------------
71 
72 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
73  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
74  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
75  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
76  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
77  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
78 /*
79  Construct a new train with general default values and input values for position and headcode.
80  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
81  This is because trains are kept in a vector and vectors erase elements during internal operations.
82  Deletion is explicit by using a special function. Increment the static class member NextTrainID
83  after setting this train's ID.
84 */
85 
86 {
87  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
88  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
89  AnsiString(TrainModeIn));
90  // AutoControl = true;//all trains start in auto control
91  UpdateCounter = 0;
92  TimeTimeLocArrived = false;
93  Derailed = false;
94  DerailPending = false;
95  Crashed = false;
96  StoppedAtBuffers = false;
97  StoppedAtSignal = false;
98  StoppedAtLocation = false;
99  StoppedAfterSPAD = false;
100  StoppedWithoutPower = false; // new at v2.4.0
101  StoppedForTrainInFront = false;
102  SignallerStoppingFlag = false;
103  SignallerStopped = false;
104  SignallerRemoved = false;
105  NotInService = false;
106  HoldAtLocationInTTMode = false;
107  AllowedToPassRedSignal = false;
108  CallingOnFlag = false;
109  BeingCalledOn = false;
110  DepartureTimeSet = false;
112  TimetableFinished = false;
113  LastActionDelayFlag = false;
114  OneLengthAccelDecel = false;
115  TrainCrashedInto = -1;
117  Plotted = false;
118  TrainGone = false;
119  SPADFlag = false;
120  FrontCodePtr = new Graphics::TBitmap;
121  FrontCodePtr->PixelFormat = pf8bit;
122  FrontCodePtr->Height = 8;
123  FrontCodePtr->Width = 8;
125  FrontCodePtr->Transparent = false;
126  AValue = sqrt(2 * PowerAtRail / Mass);
128  TerminatedMessageSent = false;
129  JoinedOtherTrainFlag = false;
131  FollowOnServiceRef = ""; //added at v2.12.0
132  TreatPassAsTimeLocDeparture = false; //added at v2.12.0
133  StepForwardFlag = false;
135  for(int x = 0; x < 4; x++)
136  {
137  HeadCodeGrPtr[x] = new Graphics::TBitmap;
138  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
139  HeadCodeGrPtr[x]->Height = 8;
140  HeadCodeGrPtr[x]->Width = 8;
142  HeadCodeGrPtr[x]->Transparent = false;
143  }
144  for(int x = 0; x < 4; x++)
145  {
146  BackgroundPtr[x] = new Graphics::TBitmap;
147  BackgroundPtr[x]->PixelFormat = pf8bit;
148  BackgroundPtr[x]->Height = 8;
149  BackgroundPtr[x]->Width = 8;
151  BackgroundPtr[x]->Transparent = false;
152  }
153  for(int x = 0; x < 4; x++)
154  {
156  // set here to ensure have values
157  }
158  for(int x = 0; x < 4; x++)
159  {
160  PlotElement[x] = -1; // marker for not plotted yet
161  }
162  for(int x = 0; x < 3; x++)
163  {
164  OldZoomOutElement[x] = -1; // marker for not plotted yet
165  }
167  NextTrainID++;
168 
169  // new values added to complete initialisation of all TTrain variables
170 
171  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
172  // TrainDataEntryPtr, initialise in AddTrain
174  FrontElementLength = 0;
175  EntrySpeed = 0;
176  ExitSpeedHalf = 0;
177  ExitSpeedFull = 0;
178  MaxExitSpeed = 0;
179  BrakeRate = 0;
181  FirstHalfMove = true;
182  EntryTime = 0;
183  ExitTimeHalf = 0;
184  ExitTimeFull = 0;
185  ReleaseTime = 0;
186  TRSTime = 0;
187  LastActionTime = 0;
188  Straddle = MidLag;
189  LeadElement = -1;
190  LeadEntryPos = 0;
191  LeadExitPos = 0;
192  MidElement = -1;
193  MidEntryPos = 0;
194  MidExitPos = 0;
195  LagElement = -1;
196  LagEntryPos = 0;
197  LagExitPos = 0;
198  TrainFailed = false; // added at v2.4.0
199  for(int x = 0; x < 4; x++)
200  {
201  HOffset[x] = 0;
202  VOffset[x] = 0;
203  PlotEntryPos[x] = 0;
204  }
205  OpTimeToAct = 60; // default value, new at v2.2.0
206  TimeToExit = -1;
207  ExitPair.first = -1;
208  ExitPair.second = -1;
209  MinsDelayed = 0.0; // new at v2.2.0
210  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
211  FinishJoinLogSent = false;
212  // added at v2.4.0 to prevent repeatdly logging the event
215  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
219  ZeroPowerNoCDTMessage = false;
224  TrainFailurePending = false;
225  SkippedDeparture = false;
226  ActionsSkippedFlag = false;
227  SkipPtrValue = 0;
228  TrainSkippedEvents = 0;
229  DelayedRandMins = 0; //added at v2.13.0
230  NewDelay = 0; //added at v2.13.0
231  CumulativeDelayedRandMinsOneTrain = 0; //added at v2.13.0
232  ActualArrivalTime = TDateTime(0); //added at v2.13.0
233  LastSigPassedFailed = false; //added at v2.13.0
234  Utilities->CallLogPop(648);
235 }
236 
237 // ---------------------------------------------------------------------------
238 
239 void TTrain::DeleteTrain(int Caller)
240 /*
241  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
242  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
243  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
244  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
245  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
246  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
247  No need to delete HeadCodePosition as that just points to existing bitmaps
248 */{
249  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
250  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
251  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
252  if(Display->ZoomOutFlag)
253  {
255  }
256  if(FrontCodePtr == 0)
257  {
258  throw Exception("Error in attempting to delete FrontCodePtr");
259  }
260  delete FrontCodePtr;
261  FrontCodePtr = 0;
262  for(int x = 0; x < 4; x++)
263  {
264  if(BackgroundPtr[x] == 0)
265  {
266  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
267  }
268  delete BackgroundPtr[x];
269  BackgroundPtr[x] = 0;
270  }
271  for(int x = 0; x < 4; x++)
272  {
273  if(HeadCodeGrPtr[x] == 0)
274  {
275  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
276  }
277  delete HeadCodeGrPtr[x];
278  HeadCodeGrPtr[x] = 0;
279  }
280  Utilities->CallLogPop(649);
281 }
282 
283 // ---------------------------------------------------------------------------
284 
286 /*
287  Plots the train starting position on screen. Note that the check for starting on straight points &
288  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
289  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
290  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
291  Set the headcode graphics pointers from the headcode text, then check whether starting at a
292  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
293  for the continuation element. Otherwise set Lead and Mid values,
294 
295  and Lead element value unless
296  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
297  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
298  then check if a train on either Mid or Lag and if so give a warning message and return false so
299  that the calling function can delete the train. Plot the Mid element train values then do similarly
300  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
301  the train. Finally set the Plotted flag and return true.
302 */{
303  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
304  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
305 
307  // PlotStartTime = TrainController->TTClockTime;
308  FirstHalfMove = true;
309 
310  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
311  // 'claim' it for this train to prevent any other waiting trains trying to enter
313  {
314  LagElement = -1; // not to be plotted
315  LagExitPos = 0; // not to be plotted
316  LagEntryPos = 0; // not to be plotted
317  MidElement = -1; // not to be plotted
318  MidExitPos = 0; // not to be plotted
319  MidEntryPos = 0; // not to be plotted
321  LeadExitPos = 1; // will be 1 for continuation entry
322  LeadEntryPos = 0;
323 
325  MaxExitSpeed = StartSpeed; // initial value
327  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
328  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
329  if(EntrySpeed > SpeedLimit)
330  {
331  EntrySpeed = SpeedLimit;
332  }
334  {
336  }
338  // LeadElement is the element to be entered
339 
340  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
341  // can achieve ExitSpeedFull at the half braking rate.
343  {
344  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
345  if(TempEntrySpeed < EntrySpeed)
346  {
347  EntrySpeed = TempEntrySpeed;
349  }
350  }
351  Straddle = MidLag; // only for starting on a continuation
353  // no need to stop gap flashing if start on continuation
354  }
355  else // not starting at a continuation
356  {
357  LagElement = -1;
358  LagEntryPos = 0;
359  LagExitPos = 0;
366 
368  MaxExitSpeed = StartSpeed; // initial value
370  bool TempDerail = false; // dummy
371  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
373  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
374  {
375  StoppedWithoutPower = true;
376  }
377  // facing buffers check - ignore starting speed if start facing buffers
378  StoppedAtBuffers = false;
379  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
382  {
383  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
384  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
385  EntrySpeed = 0;
386  ExitSpeedHalf = 0;
387  ExitSpeedFull = 0;
388  MaxExitSpeed = 0;
389  // SetTrainMovementValues not called so set this here
390  BrakeRate = 0;
393  StoppedAtSignal = false;
394  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
395  // signal check is an 'else'
396  if(!StoppedAtLocation)
397  {
398  StoppedAtBuffers = true; // stopped at location takes precedence
399  }
400  }
401 
402  // facing continuation check - don't allow to stop even if no power
404  {
405  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
406  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
410  BrakeRate = 0;
411  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
412  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
413  }
414 
415  // Signal check
416  else if((NextElementPosition > -1) && (NextEntryPos > -1))
417  // condition check added as precaution after SloughIECC error reported by James U
418  {
419  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
420  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
421  {
422  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
423  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
424  EntrySpeed = 0;
425  ExitSpeedHalf = 0;
426  ExitSpeedFull = 0;
427  MaxExitSpeed = 0;
428  BrakeRate = 0;
431  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
432  {
433  StoppedAtSignal = true;
435  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
436  }
438  {
439  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass stop signal' is offered in popup menu rather than move
440  // forwards, but don't change the background colour so still shows as stopped at location
441  StoppedAtSignal = true;
442  }
443  }
444  else
445  {
446  StoppedAtSignal = false;
447  if(NextEntryPos > 1)
448  {
449  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
450  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
451  }
452  else
453  {
454  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
455  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
456  }
457  if(EntrySpeed > SpeedLimit)
458  {
459  EntrySpeed = SpeedLimit;
460  }
462  {
464  }
466  TDateTime TestTime = TrainController->TTClockTime; // test
467  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
468  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
469  // NextElement is the element to be entered
470 
471  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
472  // can achieve ExitSpeedFull at the half braking rate.
474  {
475  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
476  // half braking
477  if(TempEntrySpeed < EntrySpeed)
478  {
479  EntrySpeed = TempEntrySpeed;
480  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
481  }
482  }
483  }
484  }
486  {
487  throw Exception("Error, LeadElement Exit Connection is NotSet");
488  }
489  }
490  if(MidElement > -1) // will be -1 if start on continuation
491  {
492  Straddle = LeadMid;
496  {
497  for(int x = 0; x < 4; x++)
498  {
499  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
500  }
501  }
502  else
503  {
504  for(int x = 0; x < 4; x++)
505  {
507  }
508  }
509  if(TrainMode == Timetable)
510  {
512  }
513  else
514  {
516  }
518  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
519 
522 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
523  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
524  {
525  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
526  Utilities->CallLogPop(651);
527  return false;
528  }
529 */
534  PlotTrainGraphic(8, 0, Display);
535  PlotTrainGraphic(9, 1, Display);
536 
539 
540  // pick up background bitmaps [2] & [3]
541 
544 
545  PlotElement[2] = MidElement;
547  PlotElement[3] = MidElement;
549  PlotTrainGraphic(10, 2, Display);
550  PlotTrainGraphic(11, 3, Display);
551  // Plotted = true; set in PlotTrainGraphic
552  }
553  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
554  Utilities->CallLogPop(652);
555 }
556 
557 // ---------------------------------------------------------------------------
558 void TTrain::UnplotTrain(int Caller)
559 {
560  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
561  if(!Plotted)
562  {
563  return;
564  }
565  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
566 
567  if(Straddle == MidLag)
568  {
569  if(MidElement > -1)
570  {
575  // to force plot of locked route marker, needed once only for the element
576  }
577  if(LagElement > -1)
578  {
583  // to force plot of locked route marker, needed once only for the element
584  }
585  }
586  else if(Straddle == LeadMidLag)
587  {
588  if(LeadElement > -1)
589  {
592  // to force plot of locked route marker, needed once only for the element
593  }
594  if(MidElement > -1)
595  {
600  // to force plot of locked route marker, needed once only for the element
601  }
602  if(LagElement > -1)
603  {
606  // to force plot of locked route marker, needed once only for the element
607  }
608  }
609  else if(Straddle == LeadMid)
610  {
611  if(LeadElement > -1)
612  {
617  // to force plot of locked route marker, needed once only for the element
618  }
619  if(MidElement > -1)
620  {
625  // to force plot of locked route marker, needed once only for the element
626  }
627  }
628  if(LeadElement > -1)
629  {
631  }
632  if(MidElement > -1)
633  {
635  }
636  if(LagElement > -1)
637  {
639  }
640  Plotted = false;
642  Display->Update();
643  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
644  // resurrected when Update() dropped from PlotOutput etc
645  Utilities->CallLogPop(653);
646 }
647 
648 // ----------------------------------------------------------------------------
649 
650 void TTrain::UpdateTrain(int Caller)
651 /*
652  Note: Some changes made since comments written
653 
654  Brief:
655  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
656  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
657  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
658  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
659  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
660  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
661  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
662  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
663  changed to MidLag within the function and all elements moved down one, old Mid becomes
664  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
665  incremented to reflect the position the train now occupies.
666 
667  Detail:
668  Set TrainFailurePending if all conditions met
669  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
670  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
671  and return.
672  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
673  If there's a LagElement (there will be but include check for good practice - next
674  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
675  train fully on offending point - Derail set and DerailPanding reset, train background
676  colour changed (note that BackgroundColour is a property of the train itself) then return.
677  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
678  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
679  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
680  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
681  if LeadElement is a fouled trailing point.
682  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
683  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
684  replotting the last background segment and checking whether the element is a bridge or crossover with the other
685  track in a route, in which case the route colour is replotted.
686  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
687  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
688  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
689  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
690  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
691  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
692  train can be deleted by the calling function, and the function returns.
693  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
694  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
695  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
696  basic red aspect.
697 
698  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
699  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
700  regardless of direction, and with the correct front code colour.
701 
702  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
703  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
704  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
705  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
706  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
707  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
708  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
709 
710  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
711  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
712  changed similarly. The function then returns.
713 
714  If Crashed is not set then Straddle is incremented and the function returns.
715 */
716 
717 {
718  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
719  UpdateCounter++;
720  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
721  if(UpdateCounter >= 100)
722  {
723  UpdateCounter = 0;
724  }
725  int RandRange = (TrainController->MTBFHours * 3600) / 53;
726 
727  // MTBFHours is in timetable clock hours, min value is 1 & max value is 9,999 (integer values on input)
728  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
729  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
730  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
731  // RandomFailureCounter value is fixed for a full cycle of train updates so this
732  // makes sure there's no bunching of failures as there is for a fixed comparison number
733  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
734  // gives a random number between 0 and 16384 (defined as RAND_MAX in stdlib.h)
735  {
736  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
737  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
738  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
739  // don't fail if:
740  // (a) on a continuation (entering or leaving);
741  // (b) already failed;
742  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
743  // (d) train terminated;
744  // (e) crashed or derailed; or
745  // (f) under signaller control and stopped.
746  // (g) TreatPassAsTimeLocDeparture is true //added at v2.12.0
747  {
748  if((random(RandRange) == 0) && !TreatPassAsTimeLocDeparture)
749  // max value for RandRange is over 2x10^9
750  {
751  // here if failure due
752  TrainFailurePending = true;
753  // fail when PlotElements set to proper Lead & Mid Elements
754  }
755  }
756  }
757 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
758  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
759  {
760  StoppedWithoutPower = true;
761  }
762 */
763 // float TimeToExit; //added at v2.10.0 Removed these so original values retained - used when train on continuation
764 // THVShortPair ExitPair; //added at v2.10.0
765  int LockedVectorNumber;
766  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
767  // default values - these needed for route checker below
768  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
769 
771  {
773  }
774  if(Crashed || Derailed)
775  {
777  {
778  PlotTrain(7, Display);
779  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
780  Display->Update();
781  }
782  OpTimeToAct = 0.0;
783  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
784  Utilities->CallLogPop(1017);
785  return; // no further action, user has to remove or work around
786  }
788  {
790  }
792  {
794  }
795  if(StoppedWithoutPower && (TrainMode == Signaller)) //added at v2.13.2 as this condition not covered - was shown as normal
796  {
798  }
800  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
801  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
802  // to move & then stop again at the same station
803  {
804  TimeTimeLocArrived = false;
805  }
806  if(!Stopped() && !SPADFlag && !TrainFailed)
807  {
809  }
810  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
811  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
812 /* old version where force a stop at buffers regardless of speed
813  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
814  else StoppedAtBuffers = false;
815 */
816 
817  // new version where crash if run into buffers
818  if(!Crashed)
819  {
820  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
821  {
822  if(ExitSpeedFull > 1)
823  {
824  Crashed = true;
828  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
829  // no need for missed action logs - will be sent when train removed
830  StoppedAtBuffers = false;
831  }
833  // stopped at location & stopped without power take precedence
834  {
835  StoppedAtBuffers = true;
836  }
837  else
838  {
839  StoppedAtBuffers = false;
840  }
841  }
842  else
843  {
844  StoppedAtBuffers = false;
845  }
846  }
847  else
848  {
849  StoppedAtBuffers = false;
850  }
851  // if crashed don't want stopped at buffers set
852 
853  // also crash if run into a level crossing that is changing or has barriers up
854  if(!Crashed)
855  {
856  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
857  {
858  int H = Track->TrackElementAt(873, LeadElement).HLoc;
859  int V = Track->TrackElementAt(874, LeadElement).VLoc;
860  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
861  {
862  Crashed = true;
866  // no need for missed action logs - will be sent when train removed
867  }
868  }
869  }
871  {
873  }
874  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
876  //if Command == "" then either TimeLoc or TimeTimeLoc so don't hold, and added last part at v2.12.0 so don't hold if have both command == pas and Treat... flag
877  {
878  HoldAtLocationInTTMode = true;
879  }
880  else if(TrainMode == Timetable)
881  {
882  HoldAtLocationInTTMode = false;
883  }
884  // in Signaller mode HoldAtLocationInTTMode not changed
885 
886  // check if departure pending & set times unless already set
887  if(TrainMode == Timetable)
888  {
890  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
891  {
892  if((ActionVectorEntryPtr->Command != "pas") && (ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) && (ActualArrivalTime > TDateTime(0)))
893  {
894  AnsiString ReasonArray[24] = {"a driver is awaited","a guard is awaited","of a medical emergency","of a technical problem","of a security issue",
895  "of a safety issue","of a disturbance","a train crew member has been taken ill","the driver has been taken ill","the guard has been taken ill",
896  "a report has been received concerning safety","a shoe has been lost under the train","of a reported theft",
897  "of an incident involving an animal","some luggage has been lost under the train","a minor repair is needed","a suspicious object has to be dealt with safely",
898  "a door is stuck open","additional stock has to be attached","a security alert","of a train fault","of an operating incident","safety checks are required",
899  "of a shortage of on train crew"};
900  //(ActionVectorEntryPtr->Command != "pas") added at v2.13.0 to rule out passes, though probably not needed
901  //(ActualArrivalTime > TDateTime(0)) added at v2.13.0 to ensure that it has been set and to dismiss trains that are present
902  //at start or have no departure time set.
903  NewDelay = 0; //section relating to random delays added at v2.13.0
904  TDateTime TimetableReleaseTime = TrainController->GetRepeatTime(0, ActionVectorEntryPtr->DepartureTime, RepeatNumber, IncrementalMinutes); //Timetable value
905  TDateTime DwellTime = TimetableReleaseTime - ActualArrivalTime; //Timetable value
906  if(DwellTime < TDateTime(30.0 / 86400))
907  {
908  DwellTime = TDateTime(30.0 / 86400);
909  }
910  int randval = random(10000);
911  if(randval != 0) //if randval == 0 or DelayMode == Nil then NewDelay will be 0 as set above
912  {
913  if(Utilities->DelayMode == Minor)
914  {
915  if(randval < Utilities->MinorDelayCutoff)
916  {
917  NewDelay = Utilities->MinorDelayFactor * log(Utilities->MinorDelayCutoff/randval);//minutes (confusingly log in C++Builder gives the natural logarithm)
918  }
919  }
920  else if(Utilities->DelayMode == Moderate)
921  {
922  if(randval < Utilities->ModerateDelayCutoff)
923  {
925  }
926  }
927  else if(Utilities->DelayMode == Major)
928  {
929  if(randval < Utilities->MajorDelayCutoff)
930  {
932  }
933  }
934  }
935 //NewDelay = 25; //test
936  if(double(TrainController->TTClockTime) <= (Utilities->LastDelayTTClockTime + 5.0/1440.0)) //if within 5 mins of last delay for any train
937  { //then don't delay. Added at v2.13.0
938  NewDelay = 0;
939  }
940  if(NewDelay < 1)
941  {
942  NewDelay = 0;
943  }
944  if(NewDelay < double(DwellTime) * 1440) //if less than scheduled dwell time then no additional delay
945  {
946  NewDelay = 0;
947  }
948  else
949  {
950  NewDelay -= double(DwellTime) * 1440;//reduce delay by dwell time
951  }
952  if(DelayedRandMins > 0)
953  {
954  DelayedRandMins -= double(DwellTime) * 1440;//reduce knock-on random delay by dwell time
955  }
956  if(DelayedRandMins < 0)
957  {
958  DelayedRandMins = 0;//can't be less than zero
959  }
961  {
962  NewDelay -= DelayedRandMins; //NewDelay is the additional delay over and above the existing knock-on delay (from earlier random delays)
963  //the formula above already includes knock-on effects
964  DelayedRandMins += NewDelay; //the new total delay, knock-on + additional
965 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //don't add here, add when depart, else this value can be > late mins
966  }
967  else
968  {
969  NewDelay = 0;
970 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //as above
971  }
972  ReleaseTime = LastActionTime + TDateTime(NewDelay / 1440); //earliest possible release time
973  if(NewDelay < 0.5) //less than the 30 secs min interval
974  {
975  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
976  }
977  if(ReleaseTime < TimetableReleaseTime)
978  {
979  ReleaseTime = TimetableReleaseTime; //back to correct time
980  NewDelay = 0;
981  DelayedRandMins = 0;
982  }
983  if(DelayedRandMins > double(ReleaseTime - TimetableReleaseTime) * 1440)
984  {
985  DelayedRandMins = double(ReleaseTime - TimetableReleaseTime) * 1440; //reduce this if time has been made up
986  }
987 
988  if(DelayedRandMins < NewDelay) //may be if reduced above, but if so need to reduce NewDelay also
989  {
991  }
992  //may be possible to simplify all the above but as it seems to work ok leave as is
993  if(int(NewDelay) > 0) //additional delay over and above knock-on effects from earlier random delays
994  {
996  if(int(NewDelay) == 1)
997  {
999  ActionVectorEntryPtr->LocationName + " by 1 minute");
1000  PerfLogForm->PerformanceLog(18, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: " + HeadCode + " delayed at " +
1001  ActionVectorEntryPtr->LocationName + " by 1 minute because of a minor technical issue");
1002  }
1003  else
1004  {
1005  Display->WarningLog(11, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " delayed at " +
1006  ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) + " minutes");
1007  if(NewDelay >= 10) //give variable reasons for >= 10 mins
1008  {
1009  int randval2 = rand() % 24; //24 reasons
1010  AnsiString Reason = ReasonArray[randval2];
1012  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1013  " minutes because " + Reason);
1014  }
1015  else
1016  {
1018  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1019  " minutes because of a minor problem");
1020  }
1021  }
1022  }
1023  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1024  ActualArrivalTime = TDateTime(0); //only run through this section once per arrival
1025  DepartureTimeSet = true;
1026  }
1027  else if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) //as was, for trains that don't have an errival time set
1028  {//if have skipped to a new service then DepartureTime will be set (in above segement when earlier train arrived)
1029  //but ArrivalTime won't be set as it is reset to 0 at end of above segement when earlier train arrived, so this segement
1030  //will run without any random delays which might cause additional complications from mixing modifications and best avoided.
1031  NewDelay = 0;
1032  DelayedRandMins = 0;
1034  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1035  {
1036  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1037  }
1038  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1039  DepartureTimeSet = true;
1040  }
1041  else if((ActionVectorEntryPtr->Command == "pas") && TreatPassAsTimeLocDeparture) //new segment at v2.12.0 to treat a pass as a departure
1042  {//for when skip to a new service at a pass location. As above this also avoids any random delays, and will avoid above segment because
1043  //departure time isn't set - it's an event time. Again random delays in this situation might cause additional complications
1044  //from mixing modifications so best avoided.
1045  NewDelay = 0;
1046  DelayedRandMins = 0;
1048  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1049  {
1050  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1051  }
1052  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1053  DepartureTimeSet = true;
1054  }
1055  }
1056  }
1057  if(TrainController->OpTimeToActUpdateCounter == 0)// && TrainController->OpActionPanelVisible) removed last condition so always calc TimeToExit
1058  {
1059  OpTimeToAct = CalcTimeToAct(0, TimeToExit, ExitPair); // called after ReleaseTime set
1060 // this->TimeToExit = TimeToExit; don't need these as values updated directly
1061 // this->ExitPair = ExitPair;
1062  // calculate every 1 sec (in real time, not timetable time) for all trains
1063  }
1064  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
1065  if(TrainMode == Timetable)
1066  {
1067  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
1068  {
1069  RemainHereLogNotSent = true;
1070  }
1072  {
1073  // ignore TimeLoc & TTLoc departures
1074  // Action logs given in functions
1076  LastActionTime + TDateTime(30.0 / 86400)))
1077  {
1078  if(ActionVectorEntryPtr->Command == "fsp")
1079  {
1080  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
1081  FrontTrainSplit(0);
1082  if(TrainFailurePending) // ok, stopped so PlotElements set
1083  {
1084  TrainHasFailed(0);
1085  }
1086  Utilities->CallLogPop(2041);
1087  return;
1088  }
1089  else if(ActionVectorEntryPtr->Command == "rsp")
1090  {
1091  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
1092  RearTrainSplit(0);
1093  if(TrainFailurePending) // ok, stopped so PlotElements set
1094  {
1095  TrainHasFailed(1);
1096  }
1097  Utilities->CallLogPop(2042);
1098  return;
1099  }
1100  else if(ActionVectorEntryPtr->Command == "Fjo")
1101  {
1102  FinishJoin(0);
1103  }
1104  else if(ActionVectorEntryPtr->Command == "jbo")
1105  {
1106  JoinedBy(0);
1107  }
1108  else if(ActionVectorEntryPtr->Command == "cdt")
1109  {
1110  ChangeTrainDirection(0, false);
1111  }
1112  else if(ActionVectorEntryPtr->Command == "Fns")
1113  {
1114  NewTrainService(0, false);
1115  }
1116  else if(ActionVectorEntryPtr->Command == "Frh")
1117  {
1118  RemainHere(0);
1119  }
1120  else if(ActionVectorEntryPtr->Command == "Fer")
1121  {
1122  TimetableFinished = true;
1123  }
1124  // other aspects of 'Fer' dealt with in TTrain::HasTrainGone()
1125  else if(ActionVectorEntryPtr->Command == "F-nshs")
1126  {
1128  }
1129  else if(ActionVectorEntryPtr->Command == "Frh-sh")
1130  {
1131  RepeatShuttleOrRemainHere(0, false);
1132  }
1133  else if(ActionVectorEntryPtr->Command == "Fns-sh")
1134  {
1136  }
1137 /*
1138  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
1139  shuttle headcode (no train creation)
1140  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1141  remain here
1142  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1143  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
1144 */
1145  }
1146  }
1147  else
1148  {
1150  {
1152  }
1153  }
1154  }
1155  if(TrainMode == Timetable)
1156  {
1157  if(StoppedAtBuffers)
1158  {
1159  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
1160  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
1161  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
1162  if(BufferLocation == "")
1163  {
1165  }
1166  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
1167  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
1168  {
1172  {
1174  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1175  // Drop missed actions so user can still use sig mode to get back on track
1177  }
1178  if(TrainFailurePending) // ok, stopped so PlotElements set
1179  {
1181  TrainHasFailed(2);
1182  }
1183  Utilities->CallLogPop(1020);
1184  return;
1185  }
1186  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !RevisedStoppedAtLoc() && (TrainController->TTClockTime >
1187  ReleaseTime))
1188  {
1191  {
1194  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1195  // Drop missed actions so user can still use sig mode to get back on track
1197  }
1198  if(TrainFailurePending) // ok, stopped so PlotElements set
1199  {
1201  TrainHasFailed(3);
1202  }
1203  Utilities->CallLogPop(1397);
1204  return;
1205  }
1206  }
1207  else
1208  {
1210  }
1211  }
1212  else
1213  {
1215  }
1216  if(TrainMode == Timetable)
1217  {
1219  {
1221  }
1223  {
1225  }
1226  }
1227  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1228  // restart after stopped for train in front
1229  int NextElementPosition, NextEntryPos;
1230 
1231  if(LeadElement > -1) // if an exit continuation then not set
1232  {
1233  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1234  {
1236  }
1237  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1238  {
1239  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1240  {
1241  LeadExitPos = 1;
1242  }
1243  else
1244  {
1245  LeadExitPos = 3;
1246  }
1247  }
1248  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1249  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1250  }
1251  else
1252  {
1253  NextElementPosition = -1;
1254  NextEntryPos = -1;
1255  }
1256  if((NextElementPosition > -1) && (NextEntryPos > -1))
1257  // may be buffers or continuation so need this check
1258  {
1259 /*
1260  Check whether calling-on conditions met:-
1261  a) approaching train has stopped at a signal but not at a location;
1262  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1263  change of direction (cdt), remaining here (Frh), or under signaller control);
1264  c) at least 1 platform available for the approaching train;
1265  d) points (if any) set for direct route into platform;
1266  e) approaching train is to stop at station;
1267  f) no more facing signals between train and platform;
1268  g) [dropped g]
1269  h) train in front preventing route being set far enough to release stop signal;
1270  i) train in front not exiting at continuation;
1271  j) signal must be within 4km of the stop platform;
1272  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily];
1273  l) no existing route conflicts with the route into the platform; and
1274  m) not failed or without power (these added at v2.10.0)
1275  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1276  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1277 */
1278  if(TrainMode == Timetable)
1279  {
1280  if(CallingOnAllowed(0)) //returns false if failed or no power (modified afer v2.9.2)
1281  {
1282  CallingOnFlag = true;
1283  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1284  }
1285  else
1286  {
1287  if(CallingOnFlag) //TrainHasFailed sets this flag to false (at v2.10.0)
1288  {
1289  if(!TrainFailed) //shouldn't be needed but include for safety at v2.10.0
1290  {
1292  }
1293  }
1294  CallingOnFlag = false;
1295  }
1296  }
1297  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !RevisedStoppedAtLoc())
1298  {
1299  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1300  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1301  // sets StoppedAtSignal again & train doesn't move
1302  StoppedAtSignal = false;
1303  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1304  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1305  // LeadMidLag and front of train was on LeadElement (after the current move)
1307  EntrySpeed = 0;
1309  FirstHalfMove = true;
1310  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1311  // NextElement is the element to be entered
1312  }
1314  {
1315  if(ClearToNextSignal(0))
1316  {
1317  StoppedForTrainInFront = false;
1318  BeingCalledOn = false;
1319  EntrySpeed = 0;
1321  FirstHalfMove = true;
1322  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1323  }
1324  else
1325  {
1326  if(TrainFailurePending) // ok, stopped so PlotElements set
1327  {
1328  TrainHasFailed(4);
1329  }
1330  Utilities->CallLogPop(1097);
1331  return;
1332  }
1333  }
1334  }
1335  if((Straddle == MidLag) && (LeadElement != -1))
1336  // later check only for Straddle == LeadMid, so need this check here for initial train start
1337  {
1339  }
1340 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1341  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1342  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1343  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1344  which could be when start as Snt.
1345  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1346  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1347  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1348  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1349  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1350  reached.
1351  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1352  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1353  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1354  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1355  sending a message to the performancelog.
1356 */
1357 
1358  if(TrainMode == Timetable)
1359  {
1361  {
1362  if(BeingCalledOn)
1363  {
1364  StoppedForTrainInFront = true;
1365  }
1367  {
1369  }
1371  {
1372  // value updated at every scheduled departure & arrival
1374  AnsiString StationName;
1376  {
1378  }
1380  {
1382  }
1383  else
1384  {
1385  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1386  }
1387  EntrySpeed = 0;
1391  FirstHalfMove = true;
1392  StoppedAtLocation = false;
1393 
1394  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1395  {
1396  StoppedWithoutPower = true;
1397  }
1398  if((NextElementPosition > -1) && (NextEntryPos > -1))
1399  // condition check added for SloughIECC error reported by James U
1400  {
1401  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1402  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1403  {
1404  StoppedAtSignal = true;
1405  if(!StoppedWithoutPower)
1406  // if stopped without power just keep existing background colour
1407  {
1409  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1410  }
1411  }
1412  }
1414  {
1415  TimeTimeLocArrived = false;
1416  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1417  // no warning for TimeTimeLoc departure
1418  }
1419  else if(TreatPassAsTimeLocDeparture) //added at v2.12.0 so late/early/on time mins recorded accurately
1420  {
1421  LogAction(36, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->EventTime, ActionVectorEntryPtr->Warning); //EventTime because the real event is a pass
1422  }
1423  else //must be TimeLoc departure
1424  {
1426  }
1427  TreatPassAsTimeLocDeparture = false; //added at v2.12.0, reset after train departs
1428  DepartureTimeSet = false;
1429  // no need to set LastActionTime for a departure
1430  //deal here with departure pointer change, increment if SkippedDeparture
1431  CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //only add these after late mins added (in LogAction)
1432 
1433  if(SkippedDeparture)
1434  {
1437  TrainSkippedEvents = 0;
1438  SkippedDeparture = false;
1439  SkipPtrValue = 0;
1440  ActionsSkippedFlag = false;
1441  }
1442  else
1443  {
1445  }
1446  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1447  // note
1448 /*
1449  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1450  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1451  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1452  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1453  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1454  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1455  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1456 */
1458  {
1459  StoppedAtBuffers = true;
1460  }
1461  else if(!StoppedWithoutPower)
1462  // if buffers or no power, don't set values
1463  {
1465  {
1466  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1467  // NextElement is the element to be entered
1468  }
1469  else
1470  {
1472  // use LeadElement for an exit continuation
1473  }
1474  }
1475  }
1476  }
1477  }
1478  if(Straddle == LeadMidLag) //train on a half element
1479  {
1481  {
1482  Utilities->CallLogPop(654);
1483  return;
1484  }
1485  }
1486  else //train fully on 2 elements
1487  {
1489  {
1490  Utilities->CallLogPop(655);
1491  return;
1492  }
1493  }
1494  if((LeadElement > -1) && (MidElement > -1))
1495  {
1497  {
1498  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1499  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1500  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1501  SignallerStoppingFlag = false;
1502  StepForwardFlag = false;
1503  }
1504  }
1505  if(Stopped())
1506  // this is what prevents another movement if the train is stopped
1507  {
1508  if(TrainFailurePending) // ok, stopped so PlotElements set
1509  {
1510  TrainHasFailed(5);
1511  }
1512  BrakeRate = 0;
1513  Utilities->CallLogPop(656);
1514  return;
1515  }
1516 
1517  // HERE WHEN READY FOR NEXT MOVE
1518 
1519  //added at v2.10.0 to set SPADFlag if red signal immediately ahead (as it will be if in a locked route)
1520  //check if due to run past a red signal & if so set SPADFlag (SetTrainMovementValues & its SPAD check only called when arrive fully on 2 elements)
1521  if(Straddle == LeadMid) //fully on 2 elements
1522  {
1523  if(LeadElement > -1)
1524  {
1525  if(Track->TrackElementAt(1402, LeadElement).Conn[LeadExitPos] > -1)
1526  {
1528  if(TIF.TrackType == SignalPost)
1529  {
1530  int TIFEntryPos = Track->TrackElementAt(1405, LeadElement).ConnLinkPos[LeadExitPos];
1531  int TIFExitPos = 0;
1532  if(TIFEntryPos == 0)
1533  {
1534  TIFExitPos = 1;
1535  }
1536  if((TIF.Config[TIFExitPos] == Signal) && TIF.Attribute == 0 && (ExitSpeedHalf > 1) && !AllowedToPassRedSignal) //use ExitSpeedHalf as may have been stopped at signal so entryspeed is 0
1537  {
1538  SPADFlag = true; // user has to intervene to reset & restart after spad
1539  }
1540  }
1541  }
1542  }
1543  }
1544 
1545  // check for train in front & if so stop at next access (when train fully on element next to train)
1546  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1547  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1548  // variable TrainInFrontInSignallerModeFlag
1549  {
1550  if(LeadElement > -1)
1551  {
1552  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1553  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1554  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1555  // true if another train on NextEntryPos track whether bridge or not
1556  {
1557  StoppedForTrainInFront = true;
1558  }
1559  else
1560  {
1561  StoppedForTrainInFront = false;
1562  }
1563  }
1564  }
1565  if((Straddle == LeadMid) && SPADFlag)
1566  // give message + plot background when ready to move half past the signal
1567  {
1568  if(NextElementPosition > -1)
1569  {
1570  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1571  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1572  {
1573  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1575  // if goes past 2 signals then give message twice
1577  }
1578  }
1579  }
1580  if(Straddle == LeadMidLag)
1581  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1582  {
1583  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1584  if(SPADFlag)
1585  {
1586  if(ExitSpeedFull == 0)
1587  {
1588  StoppedAfterSPAD = true;
1589  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1590  }
1591  }
1593  {
1594  if(ExitSpeedFull == 0)
1595  {
1596  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1597  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1598  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1599  // is sent at the right time and once only.
1600  SignallerStopped = true;
1601  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1602  StepForwardFlag = false;
1603  SignallerStoppingFlag = false;
1604  TTrackElement TE;
1605  AnsiString Loc = "";
1606  bool LocNamed = false;
1607  if(LeadElement > -1)
1608  {
1609  TE = Track->TrackElementAt(782, LeadElement);
1610  if(TE.ActiveTrackElementName != "")
1611  {
1612  Loc = TE.ActiveTrackElementName;
1613  LocNamed = true;
1614  }
1615  else
1616  {
1617  Loc = "track element " + TE.ElementID;
1618  }
1619  }
1620  if((MidElement > -1) && !LocNamed)
1621  {
1622  TE = Track->TrackElementAt(783, MidElement);
1623  if(TE.ActiveTrackElementName != "")
1624  {
1625  Loc = TE.ActiveTrackElementName;
1626  LocNamed = true;
1627  }
1628  else if(Loc == "")
1629  {
1630  Loc = "track element " + TE.ElementID;
1631  }
1632  }
1633  if(Loc == "")
1634  {
1635  Loc = "outside railway";
1636  // must have stopped after left at a continuation (because both lead & mid == -1)
1637  }
1638  else
1639  {
1640  Loc = "at " + Loc;
1641  }
1642  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1643  }
1644  }
1645  if(LeadElement > -1) // if an exit continuation then not set
1646  {
1647  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1648  {
1650  }
1651  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1652  {
1653  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1654  {
1655  LeadExitPos = 1;
1656  }
1657  else
1658  {
1659  LeadExitPos = 3;
1660  }
1661  }
1662  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1663  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1664  }
1665  else
1666  {
1667  NextElementPosition = -1;
1668  NextEntryPos = -1;
1669  }
1672  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1673 
1674  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1675  {
1676  StoppedWithoutPower = true;
1677  }
1678  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1679  // may be buffers or continuation. SPADFlag added at v2.1.0
1680  // so don't override the SPAD colour & don't set StoppedAtSignal
1681  {
1682  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1683  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !RevisedStoppedAtLoc())
1684  {
1685  StoppedAtSignal = true;
1686  if(!StoppedWithoutPower)
1687  // leave background as is if no power, but set StoppedAtSignal
1688  {
1690  }
1691  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1692  }
1693  }
1694  if(!Stopped())
1695  {
1696  if((NextElementPosition > -1) && (NextEntryPos > -1))
1697  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1698  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1699  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1700  // function for fuller explanation
1701  {
1702  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1703  // NextElement is the element to be entered
1704  }
1705  // follow the continuation exits:-
1706  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1707  {
1709  // Use LeadElement for calcs if lead is a continuation
1710  }
1711  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1712  {
1714  // Use MidElement for calcs if mid is a continuation
1715  }
1716  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1717  {
1719  // Use LagElement for calcs if lag is a continuation
1720  }
1721  }
1722  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1723  if((AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute))
1724  // Trains may not be in a route
1725  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1726  {
1727  // NB if LeadElement == -1 then the above test returns NoRoute
1728  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1729  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1730  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1731  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1732  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1733  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1734  FirstPair.second).GetELink() == TempELink))
1735  {
1736  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1737  }
1738  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1739  SecondPair.second).GetELink() == TempELink))
1740  {
1741  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1742  }
1743  }
1744  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1745  // Trains may not be in a route
1746  {
1747  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1748  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1749  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1750  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1751  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1752  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1753  FirstPair.second).GetELink() == TempELink))
1754  {
1755  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1756  }
1757  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1758  SecondPair.second).GetELink() == TempELink))
1759  {
1760  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1761  }
1762  }
1763  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1764  // Trains may not be in a route
1765  {
1766  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1767  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1768  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1769  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1770  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1771  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1772  FirstPair.second).GetELink() == TempELink))
1773  {
1774  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1775  }
1776  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1777  SecondPair.second).GetELink() == TempELink))
1778  {
1779  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1780  }
1781  AllRoutes->CheckMapAndRoutes(8); // test
1782  }
1783  if(LagElement > -1)
1784  // not entering at a continuation so can deal with train leaving the lag element
1785  {
1787  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1788  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1789  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1790 
1791  TPrefDirElement PrefDirElement;
1792  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1793  // as this is a 16x16 graphic
1795  {
1797  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1798  }
1799  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1800  {
1801  int RouteNumber;
1802  TrainGone = true;
1803  // flag to indicate train to be deleted - outside this function
1805  {
1806  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1807  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1808  // calc distance from & inc last signal to exit
1809  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1810  int NewLastElement = 0, NewLastExitPos = 0;
1811  // need above because can't change LastElement & LastExitPos until both new values obtained
1812  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1813  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1814  LastElement).TrackType != Points))
1815  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1816  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1817  // leave CumDistance as it was in these circumstances.
1818  {
1819  if(LastExitPos < 2)
1820  {
1821  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1822  }
1823  else
1824  {
1825  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1826  }
1827  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1828  if(NewLastElement == -1)
1829  // this will catch buffers or any other connection failure
1830  {
1831  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1832  }
1833  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1834  if(NewLastExitPos == -1)
1835  {
1836  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1837  }
1838  LastElement = NewLastElement;
1839  LastExitPos = NewLastExitPos;
1840  }
1841  // if at signal add this in too
1842  if(CumDistance < 1200)
1843  {
1844  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1845  }
1846  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1847  // else use 1200m - CumDistance
1848  int FirstDistance = 0;
1849  if(CumDistance >= 1200)
1850  {
1851  FirstDistance = 100;
1852  }
1853  else
1854  {
1855  FirstDistance = 1200 - CumDistance;
1856  }
1857  if(FirstDistance < 100)
1858  {
1859  FirstDistance = 100; // don't allow < 100
1860  }
1861  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1862  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1863  if(ExitSpeedFull > 20.0)
1864  {
1865  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1866  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1867  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1868  // 4320.0 = 3.6 * 1200, .0 to make it a double
1869  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1870  }
1871  else
1872  {
1873  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1874  ContinuationAutoSigEntry.SecondDelay = 120.0;
1875  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1876  }
1877  ContinuationAutoSigEntry.AccessNumber = 0;
1878  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1880  {
1882  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1883  VectorIT++)
1884  {
1885  if(VectorIT->RouteNumber == RouteNumber)
1886  {
1887  // another train has passed out of same route so erase earlier entry
1888  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1889  break;
1890  }
1891  }
1892  }
1893  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1894  }
1896  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1897  Display->Update();
1898  // need to keep this since Update() not called for PlotSmallOutput as too slow
1899  Utilities->CallLogPop(659);
1900  return;
1901  }
1902  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1903  if(LeadElement > -1)
1904  {
1905  TTrackElement &TE = Track->TrackElementAt(224, LeadElement); //added at v2.13.0 for brevity
1906  if(TE.Config[LeadExitPos] == Signal)
1907  // changed to lead so reset early
1908  {
1909  LastSigPassedFailed = false; //used to cancel route elements up to next signal for autosigs route
1910  TE.Attribute = 0; // red
1911  int RouteNumber; //only used for autosigs routes
1912  //add chance to fail when train passes a signal
1913  if((random(Utilities->SignalChangeEventsPerFailure) == 0) && !TE.Failed && (Utilities->DelayMode != Nil) &&
1914  (TrainMode == Timetable) && !TE.CallingOnSet) //can't fail twice and a calling on signal can't fail
1915  {
1917  IFE.TVPos = LeadElement;
1918  TE.Failed = true;
1919  Display->WarningLog(19, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Signal failed at " + TE.ElementID);
1920  PerfLogForm->PerformanceLog(42, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: Signal failed at " + TE.ElementID);
1921  TrainController->StopTTClockMessage(129, "Signal at " + TE.ElementID +
1922  " failed when changing aspect.\nTrains can only pass under signaller control.");
1923  AllRoutes->RebuildRailwayFlag = true; //force ClearandRebuildRailway at next clock tick
1924  LastSigPassedFailed = true;
1925  //set repair time, random value in minutes between 10 and 179
1926  double FailureMinutes = double(random(Utilities->MaxRandomRepairTime) + Utilities->FixedMinRepairTime); //between 10 and 179 minutes at random
1927  TDateTime RepairTime = TrainController->TTClockTime + TDateTime(FailureMinutes / 1440);
1928  IFE.RepairTime = RepairTime;
1930  Track->FailedSignalsVector.push_back(IFE); //rearwards signals will be set when LagElement leaves signal
1931  }
1932  TE.CallingOnSet = false;
1933  // don't plot if zoomed out
1934  if(!Display->ZoomOutFlag)
1935  {
1937  }
1938  // covers signal resetting in same direction
1939  }
1940  }
1942  {
1943  AllRoutes->RebuildRailwayFlag = true; //added at v2.13.0 to replot signal after train left in case it had failed
1944  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1945  {
1946  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1947  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1948  TPrefDirElement PrefDirElement;
1949  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1951  {
1953  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1954  }
1956  LockedVectorNumber)))
1957  {
1959  }
1960  }
1961  }
1962  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1963  {
1964  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1966  // don't plot if zoomed out
1967  if(!Display->ZoomOutFlag)
1968  {
1970  }
1971  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1972  }
1974  {
1975  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1976  {
1977  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1978  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1979  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1980  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1981  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1982  int RouteNumber;
1984  // already know it's an autosigsroute, this is just to get the RouteNumber
1985  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1986  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1987  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1988  int RouteNumber2;
1990  // already know it's an autosigsroute, this is just to get the RouteNumber
1991  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1992  // note that if not in a route (as likely) then RouteNumber2 set to -1
1993  {
1994  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1995  // this was in the 1.3.0 addition but without the condition
1996  }
1997  // end of 1.3.2 addition
1998  // end of 1.3.0.addition
1999  }
2000  TPrefDirElement PrefDirElement;
2001  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
2003  {
2005  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
2006  }
2007  }
2008  }
2009  }
2010  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
2011  if(Straddle == LeadMid)
2012  {
2013  AllowedToPassRedSignal = false;
2014  // if had been allowed to pass then at this point it will move half onto signal so can be reset
2015  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
2016  if(DerailPending)
2017  // set during last GetLeadElement, but only act on it when train fully on offending point
2018  // i.e. next time Straddle reaches LeadMid
2019  {
2020  Derailed = true;
2021  DerailPending = false;
2025  Utilities->CallLogPop(657);
2026  return;
2027  }
2034  Straddle = MidLag;
2035  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
2036  // LeadElement during this function (note that if stopped at signal then won't get this far)
2037  if(LeadElement > -1)
2038  {
2040  // i.e an exit continuation only
2041  // if don't exclude entry continuations then can't progress past it
2042  {
2043  LeadElement = -1;
2044  }
2045  else
2046  {
2047  GetLeadElement(0);
2048  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
2050  if(Stopped())
2051  {
2052  if(TrainFailurePending) // ok, stopped so PlotElements set
2053  {
2054  TrainHasFailed(6);
2055  }
2056  Utilities->CallLogPop(658);
2057  return; // i.e. don't move forward one step if next element is a red signal
2058  }
2059  }
2060  }
2061  }
2062  if(LagElement > -1)
2063  {
2064  // below are the actions required at both half moves for LagElement > -1
2066 
2067  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
2068  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
2069  // need to do this for each half element
2070 
2071  TPrefDirElement PrefDirElement;
2072  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
2073  {
2074  int RouteNumber; // holder for call below - not used
2076  {
2077  if(Utilities->clTransparent == TColor(0xFFFFFF))
2078  // change to black for a white background
2079  {
2081  // only applies for AutoSigs Route in case was locked & timed out
2082  }
2083  else
2084  // change to white for a dark background
2085  {
2087  // only applies for AutoSigs Route in case was locked & timed out
2088  }
2090  }
2091  }
2093  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
2094  // or a train on the opposite track - needs to be replotted
2095  }
2096  // update all array values
2097  HOffset[3] = HOffset[2];
2098  HOffset[2] = HOffset[1];
2099  HOffset[1] = HOffset[0];
2100  VOffset[3] = VOffset[2];
2101  VOffset[2] = VOffset[1];
2102  VOffset[1] = VOffset[0];
2103  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
2104 
2105  BackgroundPtr[3] = BackgroundPtr[2];
2106  BackgroundPtr[2] = BackgroundPtr[1];
2107  BackgroundPtr[1] = BackgroundPtr[0];
2108  BackgroundPtr[0] = TempPtr;
2109 
2110  // update headcode graphics depending on Lead entry value
2111  if(LeadElement > -1) // if Lead is -1 then stays as is
2112  {
2114  {
2115  for(int x = 0; x < 4; x++)
2116  {
2117  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
2118  }
2119  }
2120  else
2121  {
2122  for(int x = 0; x < 4; x++)
2123  {
2125  }
2126  }
2127  }
2128  if(TrainMode == Timetable)
2129  {
2131  }
2132  else
2133  {
2135  }
2137 
2138  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
2139  if(LeadElement > -1)
2140  {
2141  if(Straddle == MidLag)
2142  // just about to move half onto the new lead element
2143  {
2145  // pick up new background bitmap [0]
2147  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
2148  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
2149  // check if own ID for entry at continuation, else crashes into itself!
2150  {
2151  // OK if crossing on a bridge
2152  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
2153  if(OtherTrainEntryPos == -1)
2154  {
2155  throw Exception("Error - OtherTrainEntryPos not set");
2156  }
2157  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
2158  // LeadEntryPos for rear end crashes
2159  (LeadExitPos == OtherTrainEntryPos))
2160  // LeadExitPos for head-on crashes
2161  {
2163  Crashed = true; // only set if Straddle = MidLag
2164  CallingOnFlag = false;
2165  // in case was set, need to disable call on if call on button had been pressed
2166  }
2167  }
2168  else if(MidElement > -1) // will be -1 for continuation entries
2169  {
2170  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
2171  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
2172  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
2173  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
2174  int OtherTrainID = -1;
2175  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
2176  {
2177  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
2178  {
2179  TrainCrashedInto = OtherTrainID;
2180  Crashed = true; // only set if Straddle = MidLag
2181  CallingOnFlag = false;
2182  // in case was set, need to disable call on if call on button had been pressed
2183  }
2184  }
2185  }
2186  }
2187  else
2188  {
2190  // pick up new background bitmap [0]
2192  }
2193  PlotElement[0] = LeadElement;
2195  PlotTrainGraphic(12, 0, Display);
2196  }
2197  if(MidElement > -1)
2198  {
2199  PlotElement[2] = MidElement;
2201  PlotTrainGraphic(1, 2, Display);
2202  }
2203  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
2204  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
2205  if(Straddle == MidLag)
2206  {
2207  if(MidElement > -1)
2208  {
2209  PlotElement[1] = MidElement;
2211  PlotTrainGraphic(2, 1, Display);
2212  }
2213  if(LagElement > -1)
2214  {
2215  PlotElement[3] = LagElement;
2217  PlotTrainGraphic(3, 3, Display);
2218  }
2219  }
2220  else // Straddle == LeadMidLag
2221  {
2222  if(LeadElement > -1)
2223  {
2224  PlotElement[1] = LeadElement;
2226  PlotTrainGraphic(4, 1, Display);
2227  }
2228  if(MidElement > -1)
2229  {
2230  PlotElement[3] = MidElement;
2232  PlotTrainGraphic(5, 3, Display);
2233  }
2234  }
2235  if(Crashed)
2236  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
2237  {
2242  // in case was set, need to disable call on if call on button had been pressed
2249  Straddle = LeadMidLag;
2250  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
2251  Display->Update();
2252  // resurrected when Update() dropped from PlotOutput etc
2253  Utilities->CallLogPop(660);
2254  return;
2255  }
2256  // deal here with station stops & pass times after all replotting done but before Straddle changed
2257  if(TrainMode == Timetable)
2258  {
2259  if(Straddle == LeadMidLag)
2260  {
2261  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2262  {
2263  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2264  // to point to the location arrival entry - before a change of direction
2265  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2266  bool StopRequired = false;
2267  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2268  if(TTVPos > -1) // -1 if can't find it or if name is ""
2269  {
2270  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2271  // or a station where next element contains a train or a stop signal, if so
2272  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2273  // to test the actual track the train is on since it can't be a platform
2274  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2275  TTrackElement NextTrackElement; // default for now
2276  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2277  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2278  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2279  int NextElementEntryPos = -1;
2280  int NextElementExitPos = -1;
2281  bool TrainOnNextElement = false;
2282  bool StopSignalAtNextElement = false;
2283  if(ForwardConnection)
2284  // if no forward connection can't derive anything from it without errors
2285  {
2286  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2287  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2288  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2289  // this is only for signals so no need to worry about points ambiguity
2290  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2291  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2292  }
2293  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2294  {
2295  if(TTVPos > 0)
2296  {
2298  ActionVectorEntryPtr += TTVPos;
2299  }
2300  if(StopRequired)
2301  {
2302  StoppedAtLocation = true;
2303  StoppedAtSignal = false;
2304  // may have been set earlier at line 925 so need to reset as
2305  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2306  // in zoom out mode
2307  if(!TrainFailed)
2308  {
2310  // pale green
2311  }
2313  ActualArrivalTime = TrainController->TTClockTime; //added at v2.13.0
2315  {
2316  TimeTimeLocArrived = true;
2317  // used in case of later signaller control, when need to know
2318  // whether had arrived or not, to avoid sending the arrival
2319  // message twice, see TInterface::TimetableControl1Click
2320  }
2321  }
2322  else
2323  {
2325  }
2327  {
2329  }
2330  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2332  }
2333  }
2334  }
2335  }
2336  }
2337  if(Straddle == MidLag)
2338  {
2339  Straddle = LeadMidLag;
2340  FirstHalfMove = false;
2341  }
2342  else if(Straddle == LeadMidLag)
2343  {
2344  Straddle = LeadMid;
2345  FirstHalfMove = true;
2346  }
2347  else if(Straddle == LeadMid)
2348  {
2349  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2350  }
2351  if(TrainFailurePending) // ok, moving but PlotElements set above
2352  {
2353  TrainHasFailed(7);
2354  }
2355  Display->Update();
2356  // need to keep this since Update() not called for PlotSmallOutput as too slow
2357  Utilities->CallLogPop(661);
2358 }
2359 
2360 // ----------------------------------------------------------------------------
2361 
2362 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2363 {
2364  switch(CodeChar)
2365  {
2366  case '0':
2367  return(RailGraphics->Code0);
2368 
2369  case '1':
2370  return(RailGraphics->Code1);
2371 
2372  case '2':
2373  return(RailGraphics->Code2);
2374 
2375  case '3':
2376  return(RailGraphics->Code3);
2377 
2378  case '4':
2379  return(RailGraphics->Code4);
2380 
2381  case '5':
2382  return(RailGraphics->Code5);
2383 
2384  case '6':
2385  return(RailGraphics->Code6);
2386 
2387  case '7':
2388  return(RailGraphics->Code7);
2389 
2390  case '8':
2391  return(RailGraphics->Code8);
2392 
2393  case '9':
2394  return(RailGraphics->Code9);
2395 
2396  case 'A':
2397  return(RailGraphics->CodeA);
2398 
2399  case 'B':
2400  return(RailGraphics->CodeB);
2401 
2402  case 'C':
2403  return(RailGraphics->CodeC);
2404 
2405  case 'D':
2406  return(RailGraphics->CodeD);
2407 
2408  case 'E':
2409  return(RailGraphics->CodeE);
2410 
2411  case 'F':
2412  return(RailGraphics->CodeF);
2413 
2414  case 'G':
2415  return(RailGraphics->CodeG);
2416 
2417  case 'H':
2418  return(RailGraphics->CodeH);
2419 
2420  case 'I':
2421  return(RailGraphics->CodeI);
2422 
2423  case 'J':
2424  return(RailGraphics->CodeJ);
2425 
2426  case 'K':
2427  return(RailGraphics->CodeK);
2428 
2429  case 'L':
2430  return(RailGraphics->CodeL);
2431 
2432  case 'M':
2433  return(RailGraphics->CodeM);
2434 
2435  case 'N':
2436  return(RailGraphics->CodeN);
2437 
2438  case 'O':
2439  return(RailGraphics->CodeO);
2440 
2441  case 'P':
2442  return(RailGraphics->CodeP);
2443 
2444  case 'Q':
2445  return(RailGraphics->CodeQ);
2446 
2447  case 'R':
2448  return(RailGraphics->CodeR);
2449 
2450  case 'S':
2451  return(RailGraphics->CodeS);
2452 
2453  case 'T':
2454  return(RailGraphics->CodeT);
2455 
2456  case 'U':
2457  return(RailGraphics->CodeU);
2458 
2459  case 'V':
2460  return(RailGraphics->CodeV);
2461 
2462  case 'W':
2463  return(RailGraphics->CodeW);
2464 
2465  case 'X':
2466  return(RailGraphics->CodeX);
2467 
2468  case 'Y':
2469  return(RailGraphics->CodeY);
2470 
2471  case 'Z':
2472  return(RailGraphics->CodeZ);
2473 
2474  case 'a':
2475  return(RailGraphics->Code_a);
2476 
2477  case 'b':
2478  return(RailGraphics->Code_b);
2479 
2480  case 'c':
2481  return(RailGraphics->Code_c);
2482 
2483  case 'd':
2484  return(RailGraphics->Code_d);
2485 
2486  case 'e':
2487  return(RailGraphics->Code_e);
2488 
2489  case 'f':
2490  return(RailGraphics->Code_f);
2491 
2492  case 'g':
2493  return(RailGraphics->Code_g);
2494 
2495  case 'h':
2496  return(RailGraphics->Code_h);
2497 
2498  case 'i':
2499  return(RailGraphics->Code_i);
2500 
2501  case 'j':
2502  return(RailGraphics->Code_j);
2503 
2504  case 'k':
2505  return(RailGraphics->Code_k);
2506 
2507  case 'l':
2508  return(RailGraphics->Code_l);
2509 
2510  case 'm':
2511  return(RailGraphics->Code_m);
2512 
2513  case 'n':
2514  return(RailGraphics->Code_n);
2515 
2516  case 'o':
2517  return(RailGraphics->Code_o);
2518 
2519  case 'p':
2520  return(RailGraphics->Code_p);
2521 
2522  case 'q':
2523  return(RailGraphics->Code_q);
2524 
2525  case 'r':
2526  return(RailGraphics->Code_r);
2527 
2528  case 's':
2529  return(RailGraphics->Code_s);
2530 
2531  case 't':
2532  return(RailGraphics->Code_t);
2533 
2534  case 'u':
2535  return(RailGraphics->Code_u);
2536 
2537  case 'v':
2538  return(RailGraphics->Code_v);
2539 
2540  case 'w':
2541  return(RailGraphics->Code_w);
2542 
2543  case 'x':
2544  return(RailGraphics->Code_x);
2545 
2546  case 'y':
2547  return(RailGraphics->Code_y);
2548 
2549  case 'z':
2550  return(RailGraphics->Code_z);
2551 
2552  default:
2553  return(RailGraphics->TempHeadCode);
2554  }
2555 }
2556 
2557 // ----------------------------------------------------------------------------
2558 
2559 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2560 {
2561  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2562  if(Code.Length() != 4)
2563  {
2564  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2565  }
2566  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2567  {
2568  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2569  }
2570  if(BackgroundColour != clB5G5R5)
2571  // i.e. not the basic graphic colour as loaded from resource file
2572  {
2573  for(int x = 0; x < 4; x++)
2574  {
2576  }
2577  }
2578  Utilities->CallLogPop(1484);
2579 }
2580 
2581 // ----------------------------------------------------------------------------
2582 
2583 void TTrain::GetLeadElement(int Caller)
2584 // assumes Mid & Lag already set, sets LeadElement,
2585 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2586 {
2587  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2588  DerailPending = false;
2592  {
2593  // attr 0=straight, - links 0 & 1 (0 = lead)
2594  // attr 1=diverging, - links 2 & 3 (2 = lead)
2595  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2596  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2597 
2598  // if enter at lead, exit at whatever attr set at
2599  // if enter at lag, exit at lead, but set derail wrt attribute
2600  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2601  {
2602  LeadExitPos = 1;
2603  }
2604 
2605  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2606  // best to be on safe side
2607  else if(LeadEntryPos == 0)
2608  {
2609  LeadEntryPos = 2;
2610  LeadExitPos = 3;
2611  }
2612  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2613  {
2614  LeadEntryPos = 0;
2615  LeadExitPos = 1;
2616  }
2617  else if(LeadEntryPos == 2)
2618  {
2619  LeadExitPos = 3;
2620  }
2621  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2622  {
2623  LeadExitPos = 0;
2624  }
2625  else if(LeadEntryPos == 1)
2626  {
2627  LeadExitPos = 0;
2628  DerailPending = true;
2629  }
2630  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2631  {
2632  LeadExitPos = 0;
2633  DerailPending = true;
2634  }
2635  else if(LeadEntryPos == 3)
2636  {
2637  LeadExitPos = 0;
2638  }
2639  }
2640  else if(LeadEntryPos == 0)
2641  {
2642  LeadExitPos = 1;
2643  }
2644  else if(LeadEntryPos == 1)
2645  {
2646  LeadExitPos = 0;
2647  }
2648  else if(LeadEntryPos == 2)
2649  {
2650  LeadExitPos = 3;
2651  }
2652  else if(LeadEntryPos == 3)
2653  {
2654  LeadExitPos = 2;
2655  }
2656  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2657 /* signal check moved to Update() function
2658  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2659  && (TrackElement.Attribute == 0))//0 = red
2660  {
2661  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2662  }
2663  else
2664  {
2665  StoppedAtSignal = false;
2666  }
2667 */
2668  Utilities->CallLogPop(662);
2669 }
2670 
2671 // ----------------------------------------------------------------------------
2672 
2673 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2674 {
2675  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2676  switch(Link)
2677  {
2678  case 1:
2679  {
2680  HOffset = 0;
2681  VOffset = 0;
2682  break;
2683  }
2684 
2685  case 2:
2686  {
2687  HOffset = 4;
2688  VOffset = 0;
2689  break;
2690  }
2691 
2692  case 3:
2693  {
2694  HOffset = 8;
2695  VOffset = 0;
2696  break;
2697  }
2698 
2699  case 4:
2700  {
2701  HOffset = 0;
2702  VOffset = 4;
2703  break;
2704  }
2705 
2706  case 6:
2707  {
2708  HOffset = 8;
2709  VOffset = 4;
2710  break;
2711  }
2712 
2713  case 7:
2714  {
2715  HOffset = 0;
2716  VOffset = 8;
2717  break;
2718  }
2719 
2720  case 8:
2721  {
2722  HOffset = 4;
2723  VOffset = 8;
2724  break;
2725  }
2726 
2727  case 9:
2728  {
2729  HOffset = 8;
2730  VOffset = 8;
2731  break;
2732  }
2733 
2734  default:
2735  {
2736  throw Exception("Error in GetOffsetValues - Link value wrong");
2737  }
2738  }
2739  Utilities->CallLogPop(674);
2740 }
2741 
2742 // ---------------------------------------------------------------------------
2743 
2744 bool TTrain::LowEntryValue(int EntryLink) const
2745 {
2746 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2747  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2748 */
2749  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2750  {
2751  return(true);
2752  }
2753  else
2754  {
2755  return(false);
2756  }
2757 }
2758 
2759 // ---------------------------------------------------------------------------
2760 
2761 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2762 {
2763  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2764  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2765  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2766  // default values
2767  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2768 
2769  TAllRoutes::TRouteType RouteType;
2770 
2771  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2772 
2773  TRect SourceRect, DestRect;
2774 
2775  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2776  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2777  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2778  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2779 
2780  TempGraphic->PixelFormat = pf8bit;
2781  TempGraphic->Width = 16;
2782  TempGraphic->Height = 16;
2783  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2784 
2785  if(TempElement.TrackType == Points)
2786  {
2787  TempGraphic->Assign(TempElement.GraphicPtr);
2788  TempGraphic->Transparent = true;
2789  TempGraphic->TransparentColor = Utilities->clTransparent;
2790  if(RouteType == TAllRoutes::AutoSigsRoute)
2791  {
2792  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2793  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2794  }
2795  else
2796  {
2797  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2798  }
2799  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2800  }
2801  else if(TempElement.TrackType == GapJump) // plot set gap
2802  {
2803  if(TempElement.SpeedTag == 88)
2804  {
2805  TempGraphic->Assign(RailGraphics->gl88set);
2806  }
2807  else if(TempElement.SpeedTag == 89)
2808  {
2809  TempGraphic->Assign(RailGraphics->gl89set);
2810  }
2811  else if(TempElement.SpeedTag == 90)
2812  {
2813  TempGraphic->Assign(RailGraphics->gl90set);
2814  }
2815  else if(TempElement.SpeedTag == 91)
2816  {
2817  TempGraphic->Assign(RailGraphics->gl91set);
2818  }
2819  else if(TempElement.SpeedTag == 92)
2820  {
2821  TempGraphic->Assign(RailGraphics->gl92set);
2822  }
2823  else if(TempElement.SpeedTag == 93)
2824  {
2825  TempGraphic->Assign(RailGraphics->bm93set);
2826  }
2827  else if(TempElement.SpeedTag == 94)
2828  {
2829  TempGraphic->Assign(RailGraphics->bm94set);
2830  }
2831  else if(TempElement.SpeedTag == 95)
2832  {
2833  TempGraphic->Assign(RailGraphics->gl95set);
2834  }
2835  TempGraphic->Transparent = true;
2836  TempGraphic->TransparentColor = Utilities->clTransparent;
2837  if(RouteType == TAllRoutes::AutoSigsRoute)
2838  {
2839  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2840  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2841  }
2842  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2843  }
2844  // new for version 0.6
2845  else if(TempElement.TrackType == SignalPost)
2846  {
2847  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2848  {
2849  for(int x = 0; x < 40; x++)
2850  {
2851  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2852  // need to stop aspect
2853  {
2854  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2855  break;
2856  }
2857  }
2858  }
2859  else // normal signal
2860  {
2861  TempGraphic->Assign(TempElement.GraphicPtr);
2862  // GraphicPtr set to normal signal in a signal track element
2863  }
2864  TempGraphic->Transparent = true;
2865  TempGraphic->TransparentColor = Utilities->clTransparent;
2866  if(RouteType == TAllRoutes::AutoSigsRoute)
2867  {
2868  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2869  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2870  }
2871  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2872  }
2873  else
2874  {
2875  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2876  // can't name points gaps or signals so 'else' OK
2877  bool FoundFlag;
2878  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2879  if(FoundFlag)
2880  {
2882  {
2883  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2884  TempGraphic->Assign(RailGraphics->bmName);
2885  TempGraphic->Transparent = true;
2886  TempGraphic->TransparentColor = Utilities->clTransparent;
2887  if(RouteType == TAllRoutes::AutoSigsRoute)
2888  {
2889  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2890  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2891  }
2892  else
2893  {
2894  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2895  }
2896  // draw track on top
2897  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2898  }
2899  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2900  {
2901  TempGraphic->Assign(TempElement.GraphicPtr);
2902  TempGraphic->Transparent = true;
2903  TempGraphic->TransparentColor = Utilities->clTransparent;
2904  // note that can't be an AutoSigsRoute
2905  // now overlay the LC central portion
2906  int BDVectorPos = -1; //not used
2907  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2908  {
2909  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2910  }
2911  else
2912  {
2913  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2914  }
2915  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2916  }
2917  else
2918  {
2919  TempGraphic->Assign(TempElement.GraphicPtr);
2920  TempGraphic->Transparent = true;
2921  TempGraphic->TransparentColor = Utilities->clTransparent;
2922  if(RouteType == TAllRoutes::AutoSigsRoute)
2923  {
2924  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2925  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2926  }
2927  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2928  }
2929  }
2930  else
2931  {
2932  TempGraphic->Assign(TempElement.GraphicPtr);
2933  TempGraphic->Transparent = true;
2934  TempGraphic->TransparentColor = Utilities->clTransparent;
2935  if(RouteType == TAllRoutes::AutoSigsRoute)
2936  {
2937  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2938  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2939  }
2940  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2941  }
2942  }
2943  delete TempGraphic;
2944  Utilities->CallLogPop(675);
2945 }
2946 
2947 // ---------------------------------------------------------------------------
2948 
2949 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2950 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2951 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2952 /*
2953 
2954  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2955  {
2956  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2957  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2958  TAllRoutes::TRouteType RouteType;
2959  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2960  // default values
2961  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2962  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2963  TRect SourceRect, DestRect, ScreenSourceRect;
2964  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2965 
2966  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2967  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2968  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2969 
2970  //add text & user graphics if any to *GraphicPtr prior to adding the track
2971  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2972  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2973  int Right = Left + 8;
2974  int Bottom = Top + 8;
2975  ScreenSourceRect.init(Left, Top, Right, Bottom);
2976  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2977  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2978 
2979  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2980  TempGraphic->PixelFormat = pf8bit;
2981  TempGraphic->Width = 16;
2982  TempGraphic->Height = 16;
2983 
2984  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2985  SourceGraphic->PixelFormat = pf8bit;
2986  SourceGraphic->Width = 16;
2987  SourceGraphic->Height = 16;
2988  SourceGraphic->Transparent = true;
2989  SourceGraphic->TransparentColor = Utilities->clTransparent;
2990 
2991  if (TempElement.TrackType == Points)
2992  {
2993  TempGraphic->Assign(TempElement.GraphicPtr);
2994  TempGraphic->Transparent = true;
2995  TempGraphic->TransparentColor = Utilities->clTransparent;
2996  if (RouteType == TAllRoutes::AutoSigsRoute)
2997  {
2998  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2999  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3000  }
3001  else
3002  {
3003  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
3004  }
3005  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3006  }
3007  else if (TempElement.TrackType == GapJump) // plot set gap
3008  {
3009  if (TempElement.SpeedTag == 88)
3010  TempGraphic->Assign(RailGraphics->gl88set);
3011  else if (TempElement.SpeedTag == 89)
3012  TempGraphic->Assign(RailGraphics->gl89set);
3013  else if (TempElement.SpeedTag == 90)
3014  TempGraphic->Assign(RailGraphics->gl90set);
3015  else if (TempElement.SpeedTag == 91)
3016  TempGraphic->Assign(RailGraphics->gl91set);
3017  else if (TempElement.SpeedTag == 92)
3018  TempGraphic->Assign(RailGraphics->gl92set);
3019  else if (TempElement.SpeedTag == 93)
3020  TempGraphic->Assign(RailGraphics->bm93set);
3021  else if (TempElement.SpeedTag == 94)
3022  TempGraphic->Assign(RailGraphics->bm94set);
3023  else if (TempElement.SpeedTag == 95)
3024  TempGraphic->Assign(RailGraphics->gl95set);
3025  TempGraphic->Transparent = true;
3026  TempGraphic->TransparentColor = Utilities->clTransparent;
3027  if (RouteType == TAllRoutes::AutoSigsRoute) {
3028  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3029  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3030  }
3031  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3032  }
3033  // new for version 0.6
3034  else if (TempElement.TrackType == SignalPost)
3035  {
3036  if (TempElement.SigAspect == TTrackElement::GroundSignal)
3037  {
3038  for (int x = 0; x < 40; x++)
3039  {
3040  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
3041  // need to stop aspect
3042  {
3043  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
3044  break;
3045  }
3046  }
3047  }
3048  else // normal signal
3049  {
3050  TempGraphic->Assign(TempElement.GraphicPtr);
3051  // GraphicPtr set to normal signal in a signal track element
3052  }
3053  TempGraphic->Transparent = true;
3054  TempGraphic->TransparentColor = Utilities->clTransparent;
3055  if (RouteType == TAllRoutes::AutoSigsRoute) {
3056  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3057  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3058  }
3059  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3060  }
3061  else {
3062  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
3063  // can't name points gaps or signals so 'else' OK
3064  bool FoundFlag;
3065  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
3066  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
3067  if (FoundFlag)
3068  {
3069  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
3070  {
3071  GraphicPtr->Canvas->CopyRect(DestRect,
3072  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
3073  TempGraphic->Assign(RailGraphics->bmName);
3074  TempGraphic->Transparent = true;
3075  TempGraphic->TransparentColor = Utilities->clTransparent;
3076  if (RouteType == TAllRoutes::AutoSigsRoute)
3077  {
3078  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3079  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3080  }
3081  else
3082  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
3083  // draw track on top
3084  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3085  SourceRect);
3086  }
3087  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
3088  TempGraphic->Assign(TempElement.GraphicPtr);
3089  TempGraphic->Transparent = true;
3090  TempGraphic->TransparentColor = Utilities->clTransparent;
3091  // note that can't be an AutoSigsRoute
3092  // now overlay the LC central portion
3093  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
3094  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3095  SourceRect);
3096  }
3097  else {
3098  TempGraphic->Assign(TempElement.GraphicPtr);
3099  TempGraphic->Transparent = true;
3100  TempGraphic->TransparentColor = Utilities->clTransparent;
3101  if (RouteType == TAllRoutes::AutoSigsRoute) {
3102  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3103  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3104  }
3105  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3106  SourceRect);
3107  }
3108  }
3109  else {
3110  TempGraphic->Assign(TempElement.GraphicPtr);
3111  TempGraphic->Transparent = true;
3112  TempGraphic->TransparentColor = Utilities->clTransparent;
3113  if (RouteType == TAllRoutes::AutoSigsRoute) {
3114  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3115  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3116  }
3117  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3118  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
3119  }
3120  }
3121  delete TempGraphic;
3122  delete SourceGraphic;
3123  Utilities->CallLogPop();
3124  }
3125 */
3126 // ---------------------------------------------------------------------------
3127 
3128 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
3129 {
3130  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
3131  if(PlotElement[ArrayNumber] == -1)
3132  {
3133  Utilities->CallLogPop(676);
3134  return; // not plotted yet
3135  }
3136  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
3137  // set before plot so gap flashing stops first
3138  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3139  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
3140  // Only need to set ID for leading element, stays set until train finally leaves the element
3141  Plotted = true;
3142  Utilities->CallLogPop(677);
3143 }
3144 
3145 // ---------------------------------------------------------------------------
3146 
3147 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
3148 {
3149  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3150  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
3151 }
3152 
3153 // ---------------------------------------------------------------------------
3154 
3155 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
3156 {
3157  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
3158  HeadCode);
3159  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
3160  {
3161  Utilities->CallLogPop(678);
3162  return(true);
3163  }
3164  else
3165  {
3166  Utilities->CallLogPop(679);
3167  return(false);
3168  }
3169 }
3170 
3171 // ---------------------------------------------------------------------------
3172 
3173 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
3174 {
3175  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
3176  "," + HeadCode);
3177  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
3178  {
3179  Utilities->CallLogPop(680);
3180  return(true);
3181  }
3182  else
3183  {
3184  Utilities->CallLogPop(681);
3185  return(false);
3186  }
3187 }
3188 
3189 // ---------------------------------------------------------------------------
3190 
3191 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
3192 // test whether this train on a bridge on trackpos 0 & 1
3193 {
3194  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
3195  HeadCode);
3196  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
3197  {
3198  Utilities->CallLogPop(682);
3199  return(false);
3200  }
3201  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3203  {
3205  {
3206  throw Exception("Error, same train on two different bridge tracks");
3207  }
3208  else
3209  {
3210  Utilities->CallLogPop(684);
3211  return(true);
3212  }
3213  }
3214  else
3215  {
3216  Utilities->CallLogPop(685);
3217  return(false);
3218  }
3219 }
3220 
3221 // ---------------------------------------------------------------------------
3222 
3223 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
3224 // test whether this train on a bridge on trackpos 2 & 3
3225 {
3226  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
3227  HeadCode);
3228  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
3229  {
3230  Utilities->CallLogPop(686);
3231  return(false);
3232  }
3233  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3235  {
3236  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
3237  Utilities->CallLogPop(687);
3238  return(true);
3239  }
3240  else
3241  {
3242  Utilities->CallLogPop(688);
3243  return(false);
3244  }
3245 }
3246 
3247 // ---------------------------------------------------------------------------
3248 
3249 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3250 {
3251  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3252  AnsiString(EntryPos) + "," + HeadCode);
3253  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
3254 
3255  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
3256  if(Track->GapFlashFlag)
3257  {
3259  {
3262  Track->GapFlashFlag = false;
3263  }
3264  }
3265  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3266  {
3267  if(EntryPos == -1)
3268  {
3269  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3270  }
3271  if(EntryPos < 2)
3272  {
3274  }
3275  else
3276  {
3278  }
3279  }
3280  Utilities->CallLogPop(690);
3281 }
3282 
3283 // ---------------------------------------------------------------------------
3284 
3285 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3286 {
3287  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3288  AnsiString(EntryPos) + "," + HeadCode);
3289  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3290  {
3291  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3292  }
3293  else
3294  {
3295  if(EntryPos == -1)
3296  {
3297  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3298  }
3299  if(EntryPos < 2)
3300  {
3301  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = -1;
3302  }
3303  else
3304  {
3305  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 = -1;
3306  }
3307  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
3308  // i.e. other train on track 2&3
3309  {
3310  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
3311  }
3312  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
3313  // i.e. other train on track 1&2
3314  {
3315  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
3316  }
3317  else
3318  {
3319  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3320  }
3321  }
3322  Utilities->CallLogPop(691);
3323 }
3324 
3325 // ---------------------------------------------------------------------------
3326 
3327 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3328 {
3329  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3330  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3331  int LockedVectorNumber;
3332 
3333  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3334  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3335  {
3336  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3337  Utilities->CallLogPop(692);
3338  return;
3339  }
3340  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3341  // i.e other track is in a marked route
3342  // LinkPos doesn't have to be the entry position for the above check
3343  {
3344  TRect SourceRect, DestRect;
3345  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3346  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3347  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3348  // identify the route element for the other track
3349  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3350  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3351  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3352  int FirstELink, SecondELink = -1;
3353  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3354  // must be at least one
3355  if(RoutePair2.first > -1)
3356  {
3357  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3358  }
3359  TPrefDirElement RouteElement;
3360  // Graphics::TBitmap *RouteGraphic;
3361  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3362  // i.e. other track is in RoutePair2
3363  {
3364  if(SecondELink == -1)
3365  {
3366  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3367  }
3368  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3369  // error if both have same Link number
3370  {
3371  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3372  }
3373  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3374  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3375  }
3376  else // other track is in RoutePair1
3377  {
3378  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3379  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3380  }
3381  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3382  DestGraphic->PixelFormat = pf8bit;
3383  DestGraphic->Width = 8;
3384  DestGraphic->Height = 8;
3385  DestGraphic->Transparent = true;
3386  // has to be transparent or will overwrite the track that the train has just left
3387  DestGraphic->TransparentColor = Utilities->clTransparent;
3388  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3389  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3390  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3391  // plot locked route marker for other route if appropriate
3392  TPrefDirElement PrefDirElement; // holder for next call, unused
3393  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3394  if(StraddleValue == LeadMidLag)
3395  {
3397  PrefDirElement, LockedVectorNumber))
3398  {
3399  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3400  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3401  }
3402  }
3403  delete DestGraphic;
3404  }
3405  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3406  // also can only be a bridge or trains either have already or soon will crash
3407  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3408  {
3409  Utilities->CallLogPop(695);
3410  return;
3411  }
3412  if(ElementEntryPos > 1) // other train is on track 01
3413  {
3415  {
3417  }
3418  }
3419  else // other train is on track 23
3420  {
3422  {
3424  }
3425  }
3426  Utilities->CallLogPop(696);
3427 }
3428 
3429 // ---------------------------------------------------------------------------
3430 
3431 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3432 {
3433  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3434  AnsiString(EntryPos) + "," + HeadCode);
3435  int RouteNumber;
3436  bool WrongRoute = false;
3437  TPrefDirElement RouteElement;
3439  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3440 
3441  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3442  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3443  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3444  {
3445  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3446  {
3447  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3448  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3449  {
3450  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3451  {
3452  // don't call for stub end routes
3454  }
3455  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3456  Utilities->CallLogPop(697);
3457  return;
3458  }
3459  }
3460  // also need to check for a route on a crossing diagonal
3461  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3462  int LinkNumber = TrackElement.Link[EntryPos];
3463  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3464  {
3465  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3466  {
3467  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3468  bool LogActionErrorCalled = false;
3469  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3470  if(LinkNumber == 1)
3471  {
3472  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3473  {
3474  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3475  {
3476  // don't call for stub end routes
3478  LogActionErrorCalled = true;
3479  }
3480  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3481  }
3482  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3483  // not else in case have different routes on each diagonal, though shouldn't be possible
3484  {
3485  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3486  {
3487  // don't call for stub end routes
3489  }
3490  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3491  }
3492  }
3493 
3494  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3495  else if(LinkNumber == 3)
3496  {
3497  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3498  {
3499  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3500  {
3501  // don't call for stub end routes
3503  LogActionErrorCalled = true;
3504  }
3505  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3506  }
3507  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3508  // not else in case have different routes on each diagonal, though shouldn't be possible
3509  {
3510  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3511  {
3512  // don't call for stub end routes
3514  }
3515  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3516  }
3517  }
3518 
3519  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3520  else if(LinkNumber == 7)
3521  {
3522  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3523  {
3524  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3525  {
3526  // don't call for stub end routes
3528  LogActionErrorCalled = true;
3529  }
3530  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3531  }
3532  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3533  // not else in case have different routes on each diagonal, though shouldn't be possible
3534  {
3535  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3536  {
3537  // don't call for stub end routes
3539  }
3540  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3541  }
3542  }
3543 
3544  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3545  else if(LinkNumber == 9)
3546  {
3547  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3548  {
3549  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3550  {
3551  // don't call for stub end routes
3553  LogActionErrorCalled = true;
3554  }
3555  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3556  }
3557  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3558  // not else in case have different routes on each diagonal, though shouldn't be possible
3559  {
3560  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3561  {
3562  // don't call for stub end routes
3564  }
3565  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3566  }
3567  }
3568  }
3569  }
3570  Utilities->CallLogPop(698);
3571  return; // no route on other track or no other track
3572  }
3573  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3574  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3575  {
3576  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3577  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3578  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3579  {
3580  if(RouteElement.GetELinkPos() == EntryPos)
3581  {
3582  Utilities->CallLogPop(699);
3583  return; // right direction
3584  }
3585  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3586  {
3587  Utilities->CallLogPop(700);
3588  return; // right direction (points)
3589  }
3590  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3591  {
3592  Utilities->CallLogPop(701);
3593  return; // right direction (points)
3594  }
3595  else if(RouteElement.GetXLinkPos() == EntryPos)
3596  {
3597  WrongRoute = true;
3598  break; // wrong direction
3599  }
3600  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3601  {
3602  WrongRoute = true;
3603  break; // wrong direction
3604  }
3605  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3606  {
3607  WrongRoute = true;
3608  break; // wrong direction
3609  }
3610  }
3611  }
3612  if(!WrongRoute)
3613  {
3614  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3615  }
3616  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3617  {
3618  // don't call for stub end routes
3620  }
3621  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3622  Utilities->CallLogPop(703);
3623 }
3624 
3625 // ---------------------------------------------------------------------------
3626 
3627 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3628 {
3629  if(BackgroundColour == NewBackgroundColour)
3630  {
3631  return; // don't replot if already correct
3632 
3633  }
3634  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3635  bool ColourError = false, ColourError2 = false;
3636 
3637  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3638  if(ColourError)
3639  {
3640  ColourError2 = true;
3641  }
3642  for(int x = 0; x < 4; x++)
3643  {
3644  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3645  if(ColourError)
3646  {
3647  ColourError2 = true;
3648  }
3649  }
3650  if(ColourError2)
3651  {
3653  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3654  }
3655  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3656  // of motion
3657  for(int x = 0; x < 4; x++)
3658  {
3659  PlotTrainGraphic(6, x, Disp);
3660  }
3661  BackgroundColour = NewBackgroundColour;
3662  Display->Update();
3663  // need to keep this since Update() not called for PlotSmallOutput as too slow
3664  Utilities->CallLogPop(704);
3665 }
3666 
3667 // ---------------------------------------------------------------------------
3668 
3669 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3670 /*
3671 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3672 
3673 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3674 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3675 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3676 full-element moves.
3677 
3678 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3679 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3680 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3681 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3682 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3683 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3684 In this case set the brake rate to maximum to stop as soon as possible.
3685 
3686 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3687 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3688 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3689 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3690 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3691 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3692 
3693 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3694 first to see whether buffers or continuation) in turn is examined: first the length of the
3695 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3696 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3697 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3698 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3699 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3700 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3701 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3702 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3703 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3704 siding then again emeregency braking may be necessary and a crash may result.
3705 
3706 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3707 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3708 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3709 buffer, then the train accelerates for half the element and brakes for the other half.
3710 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3711 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3712 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3713 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3714 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3715 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3716 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3717 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3718 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3719 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3720 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3721 
3722 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3723 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3724 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3725 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3726 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3727 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3728 MaxBrakeRate.
3729 
3730 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3731 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3732 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3733 
3734 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3735 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3736 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3737 
3738 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3739 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3740 when Straddle == LeadMidLag
3741 */
3742 {
3743  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3744  AnsiString(EntryPos) + "," + HeadCode);
3745  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3746  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3747  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3748  TrainInFrontInSignallerModeFlag = false;
3749  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3750  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3751  bool SignallerStopRequired = false;
3752 
3754  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3755 
3756  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3757 
3758  OneLengthAccelDecel = false;
3759  BrakeRate = 0;
3760 
3761 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3762  if(CurrentTrackVectorPosition > -1)
3763  {
3764  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3765  {
3766  if((EntryPos == 0) || (EntryPos == 2))
3767  {
3768  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3769  {
3770  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3771  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3772  }
3773  else
3774  {
3775  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3776  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3777  }
3778  }
3779  else if(EntryPos == 1)
3780  {
3781  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3782  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3783  }
3784  else // == 3
3785  {
3786  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3787  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3788  }
3789  }
3790  else
3791  {
3792  if(EntryPos > 1)
3793  {
3794  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3795  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3796  }
3797  else
3798  {
3799  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3800  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3801  }
3802  }
3803  EntryHalfLength = CurrentElementHalfLength;
3804  FrontElementLength = 2 * CurrentElementHalfLength;
3805  }
3806  else
3807  {
3808  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3809  }
3810  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3811  {
3812  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3813  }
3814  // check if zero entry speed with another train directly in front & if so remain stopped
3815  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3816  {
3817  EntrySpeed = 0;
3818  ExitSpeedHalf = 0;
3819  ExitSpeedFull = 0;
3820  MaxExitSpeed = 0;
3821  BrakeRate = 0;
3822  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3823  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3824  StoppedForTrainInFront = true;
3825  Utilities->CallLogPop(705);
3826  return;
3827  }
3828  // new at v2.4.0 - check for stopped and zero power
3829  if((EntrySpeed < 1) && PowerAtRail < 1)
3830  {
3831  EntrySpeed = 0;
3832  ExitSpeedHalf = 0;
3833  ExitSpeedFull = 0;
3834  MaxExitSpeed = 0;
3835  BrakeRate = 0;
3836  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3837  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3838  StoppedWithoutPower = true;
3839  Utilities->CallLogPop(2125);
3840  return;
3841  }
3842 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3843  if(BeingCalledOn)
3844  {
3845  LimitingSpeed = CallOnMaxSpeed;
3846  }
3847  else
3848  {
3849  LimitingSpeed = MaximumSpeedLimit;
3850  }
3851  if(LimitingSpeed > FrontElementSpeedLimit)
3852  {
3853  LimitingSpeed = FrontElementSpeedLimit;
3854  }
3855  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3856  {
3857  LimitingSpeed = MaxRunningSpeed;
3858  }
3859  FrontElementMaxSpeed = LimitingSpeed;
3860 
3861 /*
3862  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3863  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3864  (2) V/3.6 = U/3.6 - FT;
3865  (3) S = UT/3.6 - 0.5FT^2
3866  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3867 
3868  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3869  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3870  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3871  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3872  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3873 
3874  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3875  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3876  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3877 */
3878 
3879 // check if running past a red signal without permission
3880  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3881  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3882  {
3883  SPADFlag = true; // user has to intervene to reset & restart after spad
3884  }
3885  if(!SPADFlag)
3886  {
3887  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3888  // to begin with calc the maximum exit speed (assumes accelerating) and then reduce it if necessary
3889  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3890  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3891  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3892  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3893 
3894  double ExitSpeedAtMaxBraking;
3895  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3896  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3898  {
3899  ExitSpeedAtMaxBraking = 0;
3900  }
3901  else
3902  {
3903  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3904  }
3905  double SpeedToUse;
3906  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3907  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3908  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3909  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3910  {
3911  SpeedToUse = ExitSpeedAtMaxBraking;
3912  }
3913  else
3914  {
3915  SpeedToUse = LimitingSpeed;
3916  }
3917  if(ExitSpeedFull > SpeedToUse)
3918  {
3919  ExitSpeedFull = SpeedToUse;
3920  }
3921  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3922  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3923 
3924  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3925  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3926  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3927 
3928  do
3929  {
3930  RedSignalFlag = false;
3931  BuffersFlag = false;
3932  StationFlag = false;
3933  BuffersOrContinuationNowFlag = false;
3934  ContinuationNextFlag = false;
3935  // have to reset this after the above test
3936  // add current element length to CumulativeLength
3937  CumulativeLength += (2 * CurrentElementHalfLength);
3938  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3939  {
3940  SignallerStopRequired = true;
3941  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3942  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3943  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3944  if(SignallerStopBrakeRate < TempBR)
3945  {
3946  SignallerStopBrakeRate = TempBR;
3947  }
3948  }
3949  // first check for stops within the length of the current element, where don't want any more checks & don't want
3950  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3951  // during the last loop when the NextTrackVectorPosition was the signal.
3952 
3953  // check if current element is a buffer
3954  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3955  {
3956  // no need to add in the length of this element to CumulativeLength as already included
3957  BuffersFlag = true;
3958  }
3959  // check if current element is a station stop
3960  if(TrainMode == Timetable)
3961  {
3962  bool StopRequired = false;
3963  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3964  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3965  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3966  {
3967  // no need to add in the length of element to CumulativeLength
3968  if(StopRequired)
3969  {
3970  StationFlag = true;
3971  }
3972  }
3973  }
3974  else
3975  {
3976  StationFlag = false;
3977  }
3978  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3979  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3980  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3981  {
3982  BuffersOrContinuationNowFlag = true;
3983  }
3984  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3985  {
3986  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3987  {
3988  if((EntryPos == 0) || (EntryPos == 2))
3989  {
3990  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3991  {
3992  ExitPos = 1;
3993  }
3994  else
3995  {
3996  ExitPos = 3;
3997  }
3998  }
3999  else
4000  {
4001  ExitPos = 0;
4002  }
4003  }
4004  else
4005  {
4006  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4007  }
4008  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
4009  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4010  if(NextTrackVectorPosition > -1)
4011  {
4012  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
4013  // this test & section added at v0.6
4014  {
4015  if((NextEntryPos == 0) || (NextEntryPos == 2))
4016  {
4017  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
4018  {
4019  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
4020  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
4021  }
4022  else
4023  {
4024  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
4025  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
4026  }
4027  }
4028  else if(NextEntryPos == 1)
4029  {
4030  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
4031  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
4032  }
4033  else // == 3
4034  {
4035  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
4036  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
4037  }
4038  }
4039  else
4040  {
4041  if(NextEntryPos > 1)
4042  {
4043  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
4044  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
4045  }
4046  else
4047  {
4048  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
4049  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
4050  }
4051  }
4052  }
4053  else
4054  {
4055  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
4056  }
4057  // now check for stops, first cover those where don't want to add in length of next element
4058  // check if next element is a red signal - Attr 0,
4059  // note that this doesn't apply to trains stopped at a red signal since the signal position is
4060  // CurrentTrackVectorPosition not NextTrackVectorPosition
4061  bool StopRequired;
4062  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
4063  {
4064  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
4065  {
4066  // no need to add in the length of element to CumulativeLength
4067  RedSignalFlag = true;
4068  }
4069  // next element is a red signal
4070  }
4071  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
4072  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
4073  // at least one platform element free
4075  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
4076  NextEntryPos, TrainID))
4077  {
4078  // no need to add in the length of element to CumulativeLength
4079  if(StopRequired)
4080  {
4081  StationFlag = true;
4082  }
4083  }
4084  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
4085  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
4086  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
4087  {
4088  // no need to add in the length of element to CumulativeLength
4089  TrainInFrontInSignallerModeFlag = true;
4090  }
4091  // check if next element is a buffer
4092  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
4093  {
4094  // need to add in the length of that element to CumulativeLength
4095  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
4096  BuffersFlag = true;
4097  }
4098  // check if next element is a station stop
4100  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
4101  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
4102  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
4103  {
4104  // need to add in the length of that element to CumulativeLength if a stop required
4105  if(StopRequired)
4106  {
4107  StationFlag = true;
4108  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
4109  }
4110  }
4111  }
4112  //now can decide whether need to stop over CumulativeLength
4113  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
4114  {
4115  // have to come to a stop over CumulativeLength
4116  if(CumulativeLength == FrontElementLength)
4117  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
4118  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
4119  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
4120  // and if less than EntrySpeed then skip this section (don't need any acceleration)
4121  // if not calc speed at halfway point & if less than above set half speed to this value;
4122  // use constant acceleration in calculating half time point
4123  {
4124  MaxExitSpeed = 0;
4125  double MaxHalfSpeed;
4126  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
4127  // have to halve the element length, & can't be zero or negative so no need to test
4128  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4129  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
4130  {
4131  MaxHalfSpeed = FrontElementMaxSpeed;
4132  }
4133  else
4134  {
4135  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4136  }
4137  if(MaxHalfSpeed > (2 * EntrySpeed))
4138  // use 2x to prevent kangarooing at last element when had
4139  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
4140  {
4141  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4142  0.333334);
4143  bool HalfSpeedLimited = false;
4144  if(MaxHalfSpeed < ExitSpeedHalf)
4145  {
4146  ExitSpeedHalf = MaxHalfSpeed;
4147  HalfSpeedLimited = true;
4148  }
4149  if(PowerAtRail > 1)
4150  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4151  {
4152  // [km/h/3.6 = m/s]
4153  ExitTimeHalf =
4154  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4155  }
4156  else
4157  {
4158  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4159  }
4160  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
4161  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
4162  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
4163  // by a long braking period
4164  ExitSpeedFull = 0;
4165  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
4166  if(TempBrakeRate > MaxBrakeRate)
4167  {
4168  TempBrakeRate = MaxBrakeRate;
4169  }
4170  // shouldn't be but leave in anyway
4171  if(TempBrakeRate > BrakeRate)
4172  {
4173  BrakeRate = TempBrakeRate;
4174  }
4175  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4176  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
4177  if(HalfSpeedLimited)
4178  // this is the change referred to above
4179  {
4180  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
4181  ExitTimeHalf = EntryTime + BrakingTime;
4182  ExitTimeFull = ExitTimeHalf + BrakingTime;
4183  }
4184  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
4185  Utilities->CallLogPop(1095);
4186  return;
4187  }
4188  }
4189  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
4190  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
4191  // calc th, tf, sh, & sf
4192  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
4193  if(TempBrakeRate > MaxBrakeRate)
4194  {
4195  TempBrakeRate = MaxBrakeRate;
4196  }
4197  if(TempBrakeRate > BrakeRate)
4198  {
4199  BrakeRate = TempBrakeRate;
4200  }
4201  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4202  if(SignallerStopRequired)
4203  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
4204  {
4206  {
4208  // this prevents the brakerate from reducing for a signaller stop
4209  // regardless of other conditions that may change as progress round the loop
4210  }
4211  }
4213  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
4214  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
4215  {
4217  }
4218  int TempMaxExitSpeed;
4219  // calc current value & if less than MaxExitSpeed set that to this
4220  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
4221  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4222  {
4223  MaxExitSpeedAtHalfBraking = 0;
4224  }
4225  else
4226  {
4227  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4228  }
4229  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
4230  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
4231  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4232  {
4233  TempMaxExitSpeed = FrontElementMaxSpeed;
4234  }
4235  else
4236  {
4237  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4238  }
4239  if(TempMaxExitSpeed < MaxExitSpeed)
4240  {
4241  MaxExitSpeed = TempMaxExitSpeed;
4242  }
4243  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
4244  // Cumulativelength, and Cumulativelength
4245 
4246  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
4247  {
4248  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4249  if(ExitSpeedHalfSquared < 10)
4250  {
4251  ExitSpeedHalf = 0;
4252  }
4253  else
4254  {
4255  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4256  }
4257  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4258  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4259  if(ExitSpeedFullSquared < 10)
4260  {
4261  ExitSpeedFull = 0;
4262  }
4263  else
4264  {
4265  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4266  }
4267  if((StationFlag) && (CumulativeLength == FrontElementLength))
4268  {
4269  ExitSpeedFull = 0;
4270  // force a stop for station (not for buffers or red signal)
4271  }
4272  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4273  }
4274  // new condition at v2.4.0
4275  else if(PowerAtRail <= 1)
4276  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4277  // avoid using AValue in denominator or have excessively long times
4278  {
4279  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4280  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4281  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4282 
4283  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4284  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4285  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4286  }
4287  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4288  // without the power need above condition or have hours of delay times, above added at v2.4.0
4289  {
4290  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4291  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4292  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4293  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4294  BrakeRate = 0;
4295  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4296  0.333334);
4297  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4298  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4300  // can accelerate continually over the half length
4301  {
4302  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4303  / 86400.0);
4305  // can accelerate continually over the full length
4306  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4307  {
4308  ExitTimeFull =
4309  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4310  }
4311  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4312  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4313  {
4314  // added at v0.6 as a safeguard
4315  if(MaxExitSpeed < EntrySpeed)
4316  {
4318  }
4319  // to prevent DeltaExitTimeToMaxInSecs being negative
4320  if(MaxExitSpeed < 1)
4321  {
4322  MaxExitSpeed = 1;
4323  }
4324  // to prevent divide by zero error
4325  // below as was
4327  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4328  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4329  (1.5 * AValue * AValue);
4330  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4331  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4332  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4333  }
4334  }
4335  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4336  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4337  // second halves of the element
4338  {
4339  // added at v0.6 as a safeguard
4340  if(MaxExitSpeed < EntrySpeed)
4341  {
4343  }
4344  // to prevent DeltaExitTimeToMaxInSecs being negative
4345  if(MaxExitSpeed < 1)
4346  {
4347  MaxExitSpeed = 1; // to prevent divide by zero error
4348  }
4349  // below as was
4351  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4352  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4353  (1.5 * AValue * AValue);
4354  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4355  // remaining distance to half length
4356  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4357  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4359  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4360  }
4361  }
4362  Utilities->CallLogPop(706);
4363  return;
4364  }
4365  else
4366  {
4367  if(!BuffersOrContinuationNowFlag)
4368  {
4369  if(NextSpeedLimit < LimitingSpeed)
4370  {
4371  LimitingSpeed = NextSpeedLimit;
4372  }
4373  }
4374  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4375  int TempMaxExitSpeed;
4376  // calc current value & if less than MaxExitSpeed set that to this
4377  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4378  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4379  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4380  {
4381  MaxExitSpeedAtHalfBraking = 0;
4382  }
4383  else
4384  {
4385  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4386  }
4387  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4388  {
4389  TempMaxExitSpeed = FrontElementMaxSpeed;
4390  }
4391  else
4392  {
4393  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4394  }
4395  if(TempMaxExitSpeed < MaxExitSpeed)
4396  {
4397  MaxExitSpeed = TempMaxExitSpeed;
4398  }
4399  // MaxExitSpeed is an external variable & this can reduce its value
4400  if(EntrySpeed > LimitingSpeed)
4401  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4402  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4403  {
4404  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4405  if(TempBrakeRate > MaxBrakeRate)
4406  {
4407  TempBrakeRate = MaxBrakeRate;
4408  }
4409  // shouldn't be for speedlimits since all known in advance, but include anyway
4410  if(TempBrakeRate > BrakeRate)
4411  {
4412  BrakeRate = TempBrakeRate;
4413  }
4414  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4415  }
4416  }
4417  if(!BuffersOrContinuationNowFlag)
4418  {
4419  CurrentTrackVectorPosition = NextTrackVectorPosition;
4420  EntryPos = NextEntryPos;
4421  CurrentElementHalfLength = NextElementHalfLength;
4422  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4423  {
4424  ContinuationNextFlag = true;
4425  }
4426  }
4427  }
4428  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4430  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4431  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4432  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4433  // stopping distance after it.
4434 
4435  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4436  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4437 
4438  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4439  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4440  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4441  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4442  // too late
4443 
4444  // set final braking or acc'n speed & time values
4445  if(BrakeRate > 0.01)
4446  {
4447  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4448  if(ExitSpeedHalfSquared < 10)
4449  {
4450  ExitSpeedHalf = 0;
4451  }
4452  else
4453  {
4454  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4455  }
4456  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4457  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4458  if(ExitSpeedFullSquared < 10)
4459  {
4460  ExitSpeedFull = 0;
4461  }
4462  else
4463  {
4464  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4465  }
4466  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4467  }
4468  else
4469  {
4470  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4471  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4472  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4473  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4474 
4475  BrakeRate = 0;
4476  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4477  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4478  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4480  {
4481  if(PowerAtRail > 1)
4482  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4483  {
4484  // [km/h/3.6 = m/s]
4485  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4486  / 86400.0);
4487  }
4488  else
4489  {
4490  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4491  }
4493  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4494  {
4495  if(PowerAtRail > 1)
4496  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4497  {
4498  // [km/h/3.6 = m/s]
4499  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4500  / 86400.0);
4501  }
4502  else
4503  {
4504  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4505  }
4506  }
4507  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4508  {
4509  // added at v0.6 as a safeguard
4510  if(MaxExitSpeed < EntrySpeed)
4511  {
4513  }
4514  // to prevent DeltaExitTimeToMaxInSecs being negative
4515  if(MaxExitSpeed < 1)
4516  {
4517  MaxExitSpeed = 1; // to prevent divide by zero error
4518  }
4519  // below as was
4521  double DeltaExitTimeToMaxInSecs;
4522  double DistanceToMax;
4523  if(PowerAtRail > 1) // added at v2.4.0
4524  {
4525  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4526  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4527  (1.5 * AValue * AValue);
4528  }
4529  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4530  {
4531  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4532  // these not really accurate but will be good enough
4533  DistanceToMax = EntryHalfLength;
4534  }
4535  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4536  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4537  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4538  }
4539  }
4540  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4541  {
4542  // added at v0.6 as a safeguard
4543  if(MaxExitSpeed < EntrySpeed)
4544  {
4546  }
4547  // to prevent DeltaExitTimeToMaxInSecs being negative
4548  if(MaxExitSpeed < 1)
4549  {
4550  MaxExitSpeed = 1; // to prevent divide by zero error
4551  }
4552  // below as was
4554  double DeltaExitTimeToMaxInSecs;
4555  double DistanceToMax;
4556  if(PowerAtRail > 1) // added at v2.4.0
4557  {
4558  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4559  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4560  (1.5 * AValue * AValue);
4561  }
4562  else
4563  {
4564  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4565  // these not really accurate but will be good enough
4566  DistanceToMax = EntryHalfLength / 2;
4567  }
4568  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4569  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4570  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4572  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4573  }
4574  }
4575  }
4576 
4577  else // SPADFlag set
4578  {
4580  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4581  if(ExitSpeedHalfSquared < 10)
4582  {
4583  ExitSpeedHalf = 0;
4584  }
4585  else
4586  {
4587  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4588  }
4589  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4590  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4591  if(ExitSpeedFullSquared < 10)
4592  {
4593  ExitSpeedFull = 0;
4594  }
4595  else
4596  {
4597  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4598  }
4599  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4600 
4601  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4602  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4603  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4604  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4605  // will be the stopping speed.
4606  if(ExitSpeedFull > 0)
4607  {
4608  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4609  {
4610  if((EntryPos == 0) || (EntryPos == 2))
4611  {
4612  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4613  {
4614  ExitPos = 1;
4615  }
4616  else
4617  {
4618  ExitPos = 3;
4619  }
4620  }
4621  else
4622  {
4623  ExitPos = 0;
4624  }
4625  }
4626  else
4627  {
4628  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4629  }
4630  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4631  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4632  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4633  {
4634  int NextElementLength;
4635  if(NextEntryPos > 1)
4636  {
4637  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4638  }
4639  else
4640  {
4641  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4642  }
4643  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4644  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4645  {
4646  ExitSpeedFull = 0;
4647  }
4648  }
4649  }
4650  }
4651  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4652  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4653  {
4654  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4655  if(LeadElement > -1)
4656  {
4658  // don't stop on a continuation either entering or leaving
4659  {
4662  MaxExitSpeed = LimitingSpeed;
4663  BrakeRate = 0;
4664  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4665  // assume length is 50m for ease of calc
4666  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4667  Utilities->CallLogPop(2126);
4668  return;
4669  }
4670  }
4671  else if(MidElement > -1)
4672  {
4674  {
4677  MaxExitSpeed = LimitingSpeed;
4678  BrakeRate = 0;
4679  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4680  // assume length is 50m for ease of calc
4681  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4682  Utilities->CallLogPop(2127);
4683  return;
4684  }
4685  }
4686  else if(LagElement > -1)
4687  {
4689  {
4692  MaxExitSpeed = LimitingSpeed;
4693  BrakeRate = 0;
4694  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4695  // assume length is 50m for ease of calc
4696  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4697  Utilities->CallLogPop(2128);
4698  return;
4699  }
4700  }
4701  if(EntrySpeed > 7.5) // keep going for at least another element
4702  {
4703  ExitSpeedHalf = EntrySpeed - 2.5;
4704  ExitSpeedFull = EntrySpeed - 5;
4705  MaxExitSpeed = LimitingSpeed;
4706  BrakeRate = 0;
4707  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4708  // assume length is 50m for ease of calc
4709  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4710  Utilities->CallLogPop(2129);
4711  return;
4712  }
4713  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4714  {
4715  // will appear to have slowed at steady rate
4716  ExitSpeedHalf = 0;
4717  ExitSpeedFull = 0;
4718  MaxExitSpeed = LimitingSpeed;
4719  BrakeRate = 0;
4720  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4721  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4722  StoppedWithoutPower = true;
4723  Utilities->CallLogPop(2130);
4724  return;
4725  }
4726  }
4727  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4728  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4729  Utilities->CallLogPop(707);
4730 }
4731 // ---------------------------------------------------------------------------
4732 /*
4733  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4734  {
4735  int NextExitPos;
4736  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4737  if(TimetableVector.empty()) return false;
4738  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4739  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4740  {
4741  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4742  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4743  {
4744  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4745  }
4746  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4747  {
4748  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4749  }
4750  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4751  NextElement = TempElement;
4752  }
4753  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4754  if(NextElement.TrackType == Buffers) return true;
4755  return false;//shouldn't reach here but include to prevent compiler return warning
4756  }
4757 */
4758 // ---------------------------------------------------------------------------
4759 
4760 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4761 /*
4762  returns the number by which the train ActionVectorEntryPtr needs
4763  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4764  actions when a stop or pass location has been reached before other timetabled events have been carried out. If can't find it, or Name
4765  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4766  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4767  or pass (false) the location.
4768 */{
4769  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4770  Stop = false;
4771  if(TimetableFinished || (Name == ""))
4772  {
4773  Utilities->CallLogPop(957);
4774  return(-1);
4775  }
4777 /*added the following check at v2.11.1 because of a fault found in Kevin Smith's railway (notified 02/01/22). CH01 started at
4778 Chester behind the stop position, but when it departed and this function was called it found Chester again with no cdt
4779 before it (it went round the Liverpool Loop), so it stopped again when it reached the stop position and reported all intermediate stops as having been
4780 missed. This check is for the train just having departed from the station in question, and if it has then any further stations
4781 with the same name are ignored - i.e. it stops a train from stopping at the same station twice in succession.
4782 */
4783  if(Ptr > &TrainDataEntryPtr->ActionVector.at(0))
4784  {
4785  Ptr--;
4786  if((Ptr->DepartureTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4787  {
4788  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4789  {
4790  Utilities->CallLogPop(2444);
4791  return(-1);
4792  }
4793  }
4794  }
4795  // start looking from current pointer position
4796  for(TActionVectorEntry *Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4797  {
4798  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4799  {
4800  break;
4801  }
4802  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4803  {
4804  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4805  {
4806  Stop = true;
4807  Utilities->CallLogPop(960);
4808  return (Ptr - ActionVectorEntryPtr);
4809  }
4810  }
4811  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4812  {
4813  Utilities->CallLogPop(1517);
4814  return (Ptr - ActionVectorEntryPtr);
4815  }
4816  }
4817  Utilities->CallLogPop(959);
4818  return(-1); // not found a valid entry
4819 }
4820 
4821 // ---------------------------------------------------------------------------
4822 
4824 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4825  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4826  Ignores the call-on signal.
4827 */{
4828  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4829  int ReturnVal = 0;
4830  int ElementCount = 0;
4831 /* dropped at v2.12.0 as takes up a great deal of time unnecessarily - substitute 1000 elements instead and return true (very unlikley to need to search this far [10km at min length])
4832  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4833  {
4834  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4835  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4836  }
4837 */
4838  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4839  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4840 
4841  while(true)
4842  {
4843  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4844  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4845  {
4846  ReturnVal = 1;
4847  break;
4848  }
4849  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4850  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4851  {
4852  ReturnVal = 2;
4853  break;
4854  }
4855  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4856  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4857  {
4858  ReturnVal = 3;
4859  break;
4860  }
4861 /* not needed at and after v2.12.0, see above
4862  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388, CurrentTrackVectorPosition).TrackType == Crossover))
4863  {
4864  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4865  // must be a loop - reached same point as examined earlier
4866  {
4867  ReturnVal = 4;
4868  break;
4869  }
4870  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4871  {
4872  ReturnVal = 4;
4873  break;
4874  }
4875  }
4876  else
4877  {
4878  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526, CurrentTrackVectorPosition).TempTrackMarker23))
4879  {
4880  ReturnVal = 4;
4881  break;
4882  }
4883  }
4884  if(EntryPos < 2)
4885  {
4886  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4887  }
4888  else
4889  {
4890  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4891  }
4892 */
4893 
4894  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4895  {
4896  if((EntryPos == 0) || (EntryPos == 2))
4897  {
4898  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4899  {
4900  ExitPos = 1;
4901  }
4902  else
4903  {
4904  ExitPos = 3;
4905  }
4906  }
4907  else
4908  {
4909  ExitPos = 0;
4910  }
4911  }
4912  else
4913  {
4914  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4915  }
4916  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4917  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4918  CurrentTrackVectorPosition = NextTrackVectorPosition;
4919  EntryPos = NextEntryPos;
4920  ElementCount++;
4921  if(ElementCount > 1000)
4922  {
4923  ReturnVal = 4;
4924  break;
4925  }
4926  }
4927  if(ReturnVal == 1)
4928  {
4929  Utilities->CallLogPop(708);
4930  return(false);
4931  }
4932  if(ReturnVal == 2)
4933  {
4934  Utilities->CallLogPop(709);
4935  return(true);
4936  }
4937  if(ReturnVal == 3)
4938  {
4939  Utilities->CallLogPop(946);
4940  return(true);
4941  }
4942  if(ReturnVal == 4)
4943  {
4944  Utilities->CallLogPop(947);
4945  return(true);
4946  }
4947  else
4948  {
4949  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4950  }
4951 }
4952 
4953 // ---------------------------------------------------------------------------
4954 
4956 /*
4957  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4958  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4959  change of direction (cdt), remaining here (Frh), or under signaller control);
4960  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4961  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4962  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4963  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] l) no existing route conflicts with the route into the platform,
4964  m) not failed or stopped without power
4965  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4966  change points outside the route or have a route conflict if another route is set.
4967 */{
4968  if(Track->RouteFlashFlag || TrainFailed || StoppedWithoutPower) //failed & no power conditions added at v2.10.0
4969  {
4970  return(false); // don't want to create a new route from the stop signal if one is already in construction & can't call on if failed or no power
4971  }
4972  // some of the callingon route elements may be involved
4973  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4974  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4975  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4976  int RouteStartPosition;
4977  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4978  int PlatformPosition;
4979  // the track vector position of the first stop platfrom
4980  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4981  // not used here
4982  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4983  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
4984  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
4985  // must be stopped at a signal but not at a location & still in timetable (a)
4987  // no need to check for SignallerStopped as this function only called in Timetable mode
4988  {
4989  Utilities->CallLogPop(711);
4990  return(false);
4991  }
4992  while(true)
4993  {
4994  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4995  // don't look further than 4km ahead (j)
4996  if(Distance > (4000 + LeadElementDistance))
4997  {
4998  Utilities->CallLogPop(967);
4999  return(false);
5000  }
5001  // if find another train on an element in front, before find a valid platform, return false (c)
5002  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
5003  {
5004  Utilities->CallLogPop(713);
5005  return(false);
5006  }
5007  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
5008  // be facing later on)
5009  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
5010  {
5011  // get LeadElement, if -1 return (could be exiting at continuation) (i)
5012  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
5013  if(OtherTrain.LeadElement == -1)
5014  {
5015  Utilities->CallLogPop(714);
5016  return(false);
5017  }
5018  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
5019  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
5020  {
5021  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
5022  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
5023  (OtherTrain.TrainMode == Signaller))
5024  {
5025  break;
5026  }
5027  else
5028  {
5029  Utilities->CallLogPop(955);
5030  return(false);
5031  }
5032  }
5033  else // (h)
5034  {
5035  break;
5036  }
5037  }
5038  // if reach buffers or exit continuation return false (can set route)
5039  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
5040  {
5041  Utilities->CallLogPop(716);
5042  return(false);
5043  }
5044  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
5045  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
5047  {
5048  Utilities->CallLogPop(717);
5049  return(false);
5050  }
5051  // if reach a location that isn't in timetable return false - drop this as still can't set a route
5052 /*
5053  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
5054  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
5055  {
5056  Utilities->CallLogPop(718);
5057  return false;
5058  }
5059 */
5060  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
5061  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
5062  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
5063  {
5064  if(StopRequired)
5065  {
5066  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
5067  {
5068  if(!PlatformFoundFlag)
5069  {
5070  PlatformPosition = CurrentTrackVectorPosition;
5071  }
5072  // ensure this only set once at first valid platform position - the unrestricted route will end here
5073  PlatformFoundFlag = true;
5074  }
5075  }
5076  }
5077  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
5078  // train has to be at station but that has to be before the next forward signal
5079 /*
5080  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
5081  {
5082  Utilities->CallLogPop(719);
5083  return false;
5084  }
5085 */
5086  // make sure points are followed correctly (d) & set ExitPos
5087  if(CurrentTrackElement.TrackType == Points)
5088  {
5089  if((EntryPos == 0) || (EntryPos == 2))
5090  {
5091  if(CurrentTrackElement.Attribute == 0)
5092  {
5093  ExitPos = 1;
5094  }
5095  else
5096  {
5097  ExitPos = 3;
5098  }
5099  }
5100  if(EntryPos == 1)
5101  {
5102  if(CurrentTrackElement.Attribute == 0)
5103  {
5104  ExitPos = 0;
5105  }
5106  else
5107  {
5108  Utilities->CallLogPop(720);
5109  return(false);
5110  }
5111  }
5112  if(EntryPos == 3)
5113  {
5114  if(CurrentTrackElement.Attribute == 1)
5115  {
5116  ExitPos = 0;
5117  }
5118  else
5119  {
5120  Utilities->CallLogPop(721);
5121  return(false);
5122  }
5123  }
5124  }
5125  else
5126  {
5127  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
5128  }
5129  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
5130  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
5131  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
5132  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
5133  if(ElementNumber < 2)
5134  {
5135  SkipRouteCheck = true;
5136  }
5137  else
5138  {
5139  SkipRouteCheck = false;
5140  }
5141  if(ElementNumber == 1) // the stop signal
5142  {
5143  RouteStartPosition = CurrentTrackVectorPosition;
5144  }
5145 /*
5146  if(ElementNumber == 2)
5147  {
5148  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
5149  else AutoSigs = false;
5150  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
5151  }
5152 */
5153  if(ElementNumber > 1)
5154  {
5155  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
5156  {
5157  RouteOrPartRouteSet = true;
5158  }
5159  else
5160  {
5161  RouteOrPartRouteSet = false;
5162  }
5163  }
5164  if(!SkipRouteCheck && !RouteOrPartRouteSet)
5165  {
5166  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
5167  {
5168  Utilities->CallLogPop(1859);
5169  return(false);
5170  }
5171  int ExitLink = CurrentTrackElement.Link[ExitPos];
5172  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
5173  {
5174  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
5175  {
5176  Utilities->CallLogPop(1850);
5177  return(false);
5178  }
5179  }
5180  }
5181  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
5182  if(EntryPos < 2)
5183  {
5184  Distance += CurrentTrackElement.Length01;
5185  }
5186  else
5187  {
5188  Distance += CurrentTrackElement.Length23;
5189  }
5190  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
5191  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
5192  CurrentTrackVectorPosition = NextTrackVectorPosition;
5193  EntryPos = NextEntryPos;
5194  ElementNumber++;
5195  } // while(true)
5196 
5197  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
5198  // from the stop signal (note that it may be last in an autosigs route)
5199  // a single element route at the stop signal should have been removed prior to this function being called (that called before
5200  // this in ClockTimer2)
5201 
5202  // now add elements to the CallonVector
5203  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
5204 
5205  AllRoutes->CallonVector.push_back(CallonEntry);
5206  Utilities->CallLogPop(1860);
5207  return(true); // return false if fail to set route for any reason
5208 }
5209 
5210 // ---------------------------------------------------------------------------
5211 /*
5212  bool TTrain::TimetableFinished()
5213  {
5214  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
5215  {
5216  return true;
5217  }
5218  return false;
5219  }
5220 */
5221 // ---------------------------------------------------------------------------
5222 
5223 AnsiString TTrain::GetTrainHeadCode(int Caller)
5224 
5225 {
5226  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
5227  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
5228 
5229  Utilities->CallLogPop(1452);
5230  return(RepeatHeadCode);
5231 }
5232 
5233 // ---------------------------------------------------------------------------
5234 
5235 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
5236 {
5237  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
5238  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
5239 
5240  Utilities->CallLogPop(1453);
5241  return(RepeatTime);
5242 }
5243 
5244 // ---------------------------------------------------------------------------
5245 
5246 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
5247 {
5248  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
5249  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
5250  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
5251  // first check that train is fully on the railway
5252  bool FrontValid = false, RearValid = false;
5253  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
5254 
5255  if((LeadElement == -1) || (MidElement == -1))
5256  {
5257  TrainToBeJoinedBy = NULL;
5258  Utilities->CallLogPop(2131);
5259  return(false);
5260  }
5262  {
5263  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
5264  FrontValid = true;
5265  }
5267  {
5268  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
5269  RearValid = true;
5270  }
5271  int TrainToBeJoinedByID = -1;
5272 
5273  // first check if on a 2-track element & select correct ID number if so
5274  if(FrontValid)
5275  {
5276  if(FrontAdjacentTrackElement.TrackType == Bridge)
5277  {
5279  {
5280  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5281  }
5282  else
5283  {
5284  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5285  }
5286  }
5287  else
5288  {
5289  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
5290  }
5291  }
5292  if((TrainToBeJoinedByID < 0) && RearValid)
5293  {
5294  // first check if on a 2-track element & select correct ID number if so
5295  if(RearAdjacentTrackElement.TrackType == Bridge)
5296  {
5298  {
5299  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5300  }
5301  else
5302  {
5303  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5304  }
5305  }
5306  else
5307  {
5308  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5309  }
5310  }
5311  if(TrainToBeJoinedByID < 0) // no adjacent train
5312  {
5313  TrainToBeJoinedBy = NULL;
5314  Utilities->CallLogPop(2132);
5315  return(false);
5316  }
5317  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5318  if(!TrainToBeJoinedBy->Stopped())
5319  {
5320  TrainToBeJoinedBy = NULL;
5321  Utilities->CallLogPop(2133);
5322  return(false);
5323  }
5324  Utilities->CallLogPop(2134);
5325  return(true);
5326 }
5327 
5328 // ---------------------------------------------------------------------------
5329 
5330 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
5331  TDateTime TimetableNonRepeatTime, bool Warning)
5332 /*
5333  Time = timetable time, the time adjustments for repeat trains is carried out internally
5334  Not all messages need this, if not needed a dummy value is required but not used
5335 
5336  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5337  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5338  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5339  //NB for Frh just give terminated message but without event time - don't use this function
5340  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5341  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5342  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5343  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5344  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
5345  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
5346  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5347  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5348  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5349  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5350  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5351  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5352  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5353  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5354  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5355  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5356  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass stop signal
5357  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5358  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5359  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5360  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5361  SignallerStop 06:05:40: 2F46 stopped on signaller command
5362  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5363  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5364 */{
5365  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5366  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5367  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5368  int IntMinsLate = 0;
5369 
5370  // need to set it in case MinsLate == 0, since it isn't tested for that
5371  if(ActionType == Arrive)
5372  {
5373  ActionLog = " arrived at ";
5374  }
5375  if(ActionType == Terminate)
5376  {
5377  if(TerminatedMessageSent) // to avoid it being sent twice
5378  {
5379  Utilities->CallLogPop(1104);
5380  return;
5381  }
5382  ActionLog = " terminated at ";
5383  TerminatedMessageSent = true;
5384  }
5385  if(ActionType == Depart)
5386  {
5387  ActionLog = " departed from ";
5388  }
5389  if(ActionType == Pass)
5390  {
5391  ActionLog = " passed ";
5392  }
5393  if(ActionType == Create)
5394  {
5395  ActionLog = " created at ";
5396  }
5397  if(ActionType == Enter)
5398  {
5399  ActionLog = " entered railway at ";
5400  }
5401  if(ActionType == Leave)
5402  {
5403  ActionLog = " left railway at ";
5404  }
5405  if(ActionType == FrontSplit)
5406  {
5407  ActionLog = " split from front to ";
5408  }
5409  if(ActionType == RearSplit)
5410  {
5411  ActionLog = " split from rear to ";
5412  }
5413  if(ActionType == JoinedByOther)
5414  {
5415  ActionLog = " joined by ";
5416  }
5417  if(ActionType == ChangeDirection)
5418  {
5419  ActionLog = " changed direction at ";
5420  }
5421  if(ActionType == NewService)
5422  {
5423  ActionLog = " became new service ";
5424  }
5425  if(ActionType == TakeSignallerControl)
5426  {
5427  ActionLog = " taken under signaller control at ";
5428  }
5429  if(ActionType == RestoreTimetableControl)
5430  {
5431  ActionLog = " restored to timetable control at ";
5432  }
5433  if(ActionType == RemoveTrain)
5434  {
5435  if(Crashed)
5436  {
5437  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5438  }
5439  else if(Derailed)
5440  {
5441  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5442  }
5443  else
5444  {
5445  ActionLog = " REMOVED FROM RAILWAY at ";
5446  }
5447  }
5448  if(ActionType == SignallerMoveForwards)
5449  {
5450  ActionLog = " received signaller authority to proceed";
5451  }
5452  if(ActionType == SignallerStepForward)
5453  {
5454  ActionLog = " received signaller authority to step forward";
5455  }
5456  if(ActionType == SignallerChangeDirection)
5457  {
5458  ActionLog = " changed direction under signaller control at ";
5459  }
5460  if(ActionType == SignallerPassRedSignal)
5461  {
5462  ActionLog = " received signaller authority to pass stop signal";
5463  }
5464  if(ActionType == SignallerControlStop)
5465  {
5466  ActionLog = " received signaller instruction to stop";
5467  }
5468  if(ActionType == SignallerStop)
5469  {
5470  ActionLog = " stopped on signaller instruction ";
5471  }
5472  if(ActionType == SignallerJoin)
5473  {
5474  ActionLog = " joined under signaller control by ";
5475  }
5476  if(ActionType == TrainFailure)
5477  {
5478  ActionLog = " suffered an onboard power failure at ";
5479  }
5480  if(ActionType == RepairFailedTrain)
5481  {
5482  ActionLog = " failure repaired at ";
5483  }
5484  if(ActionType == SignallerLeave)
5485  {
5486  ActionLog = " left railway under signaller control at ";
5487  }
5488  if(OtherHeadCode != "")
5489  {
5490  OtherHeadCode += " at ";
5491  }
5492  TDateTime ActualTime = TrainController->TTClockTime;
5493 
5494  if(Warning)
5495  {
5496  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5497  WarningBaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName; //added time at v2.13.0
5498  }
5499  else
5500  {
5501  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5502  }
5503  bool TimePerformance = true;
5504 
5505  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5506  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5507  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5508  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5509  // SignallerJoin & RepairFailedTrain new at v2.4.0
5510  {
5511  TimePerformance = false;
5512  }
5513  if(TimePerformance)
5514  {
5515  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5516  MinsDelayed = float(MinsLate);
5517  if(ActionType == Pass) //added at v2.9.2 to prevent time to act increasing suddenly for early pass times then becoming 'NOW' when stops at signal
5518  {
5519  MinsDelayed = 0;
5520  }
5521  // new v2.2.0 for OpActionPanel, can be positive or negative
5522  if(ActionType == Arrive)
5523  {
5525  }
5526  // since train has just arrived this value is the
5527  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5528  // subtracted from later stop recoverable times.
5529  if(MinsLate < 0)
5530  {
5531  IntMinsLate = int(ceil(MinsLate));
5532  }
5533  if(MinsLate > 0)
5534  {
5535  IntMinsLate = int(floor(MinsLate));
5536  }
5537  if(IntMinsLate == 0)
5538  {
5539  PerfLog = " on time";
5540  }
5541  else if(IntMinsLate == 1)
5542  {
5543  PerfLog = " 1 minute late";
5544  }
5545  else if(IntMinsLate == -1)
5546  {
5547  PerfLog = " 1 minute early";
5548  }
5549  else if(IntMinsLate > 1)
5550  {
5551  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5552  }
5553  else if(IntMinsLate < -1)
5554  {
5555  int PosIntMinsLate = -IntMinsLate;
5556  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5557  }
5558  if(LocationName.Pos('-') > 0)
5559  {
5560  PerfLog = "," + PerfLog;
5561  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5562  }
5563  PerfLogForm->PerformanceLog(0, BaseLog + PerfLog);
5564  }
5565  else
5566  {
5567  PerfLogForm->PerformanceLog(1, BaseLog);
5568  }
5569  if(Warning)
5570  {
5571  Display->WarningLog(0, WarningBaseLog);
5572  }
5573  // update statistics
5574  if((ActionType == Arrive) && (IntMinsLate == 0))
5575  {
5577  }
5578  else if((ActionType == Arrive) && (IntMinsLate > 0))
5579  {
5581  TrainController->TotLateArrMins += IntMinsLate;
5582  }
5583  else if((ActionType == Arrive) && (IntMinsLate < 0))
5584  {
5586  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5587  }
5588 
5589  else if((ActionType == Pass) && (IntMinsLate == 0))
5590  {
5592  }
5593  else if((ActionType == Pass) && (IntMinsLate > 0))
5594  {
5596  TrainController->TotLatePassMins += IntMinsLate;
5597  }
5598  else if((ActionType == Pass) && (IntMinsLate < 0))
5599  {
5601  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5602  }
5603 
5604  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5605  {
5607  }
5608  else if((ActionType == Leave) && (IntMinsLate > 0))
5609  {
5611  TrainController->TotLateExitMins += IntMinsLate;
5612  }
5613  else if((ActionType == Leave) && (IntMinsLate < 0))
5614  {
5616  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5617  }
5618 
5619  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5620  {
5622  }
5623  else if((ActionType == Depart) && (IntMinsLate > 0))
5624  {
5626  TrainController->TotLateDepMins += IntMinsLate;
5627  }
5628  Utilities->CallLogPop(968);
5629 }
5630 
5631 // ---------------------------------------------------------------------------
5632 
5633 void TTrain::TrainHasFailed(int Caller)
5634 {
5635  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5636  if(Crashed || Derailed || DerailPending)
5637  {
5638  TrainFailurePending = false;
5639  Utilities->CallLogPop(2135);
5640  return;
5641  }
5642  AnsiString LocName = "";
5643 
5644  if(LeadElement > -1)
5645  {
5647  }
5648  if((LocName == "") && (MidElement > -1))
5649  {
5651  }
5652  if((LocName == "") && LeadElement > -1)
5653  {
5654  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5655  }
5656  if((LocName == "") && (MidElement > -1))
5657  {
5658  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5659  }
5660  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5661  TrainFailed = true;
5662  TrainFailurePending = false;
5663  CallingOnFlag = false; //added at v2.10.0
5665  PowerAtRail = 0.08;
5666  AValue = sqrt(2 * PowerAtRail / Mass);
5668  // TrainFailed only called when PlotElements properly set to Lead, Mid & Lag elements
5669  if(Stopped())
5670  {
5671  EntrySpeed = 0;
5672  ExitSpeedHalf = 0;
5673  ExitSpeedFull = 0;
5674  MaxExitSpeed = 0;
5675  BrakeRate = 0;
5676  StoppedWithoutPower = true;
5677  }
5679  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5680  // true for warning, TDateTime not used
5681  Utilities->CallLogPop(2136);
5682 }
5683 
5684 // ---------------------------------------------------------------------------
5685 
5686 void TTrain::FrontTrainSplit(int Caller)
5687 {
5688 /*
5689  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5690  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5691  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5692  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5693 */
5694  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5695  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5696  if(PowerAtRail < 1)
5697  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5698  {
5700  {
5701  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5702  }
5704  Utilities->CallLogPop(2137);
5705  return;
5706  }
5707  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5708 
5709  if(LocationName == "")
5710  {
5711  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5712  }
5713  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5714  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5715  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5717 
5718  // determine all positions & exits
5719  if(LocationName != "")
5720  {
5721  // if message given only call at ~5 sec intervals
5723  {
5724  FirstNamedElementPos = LeadElement;
5725  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5726  // check if possible with LeadElement as First
5727  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5728  {
5729  FirstNamedElementPos = MidElement;
5730  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5731  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5732  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5733  {
5735  {
5736  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5738  }
5739  Utilities->CallLogPop(1009);
5740  return;
5741  }
5742  }
5743  else
5744  {
5745  // if first is possible then check if all 4 positions at location, and if not try the second
5746  int LeadPosA = FirstNamedElementPos;
5747  int LeadPosB = FirstNamedLinkedElementPos;
5748  int LeadPosC = SecondNamedElementPos;
5749  int LeadPosD = SecondNamedLinkedElementPos;
5750  // count number of positions that are at the location
5751  int LeadNumAtLoc = 0;
5752  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5753  {
5754  LeadNumAtLoc++;
5755  }
5756  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5757  {
5758  LeadNumAtLoc++;
5759  }
5760  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5761  {
5762  LeadNumAtLoc++;
5763  }
5764  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5765  {
5766  LeadNumAtLoc++;
5767  }
5768  if(LeadNumAtLoc < 4)
5769  {
5770  FirstNamedElementPos = MidElement;
5771  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5772  SecondNamedLinkedElementPos)) // restore originals
5773  {
5774  FirstNamedElementPos = LeadPosA;
5775  FirstNamedLinkedElementPos = LeadPosB;
5776  SecondNamedElementPos = LeadPosC;
5777  SecondNamedLinkedElementPos = LeadPosD;
5778  }
5779  else // count number at location
5780  {
5781  int MidNumAtLoc = 0;
5782  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5783  {
5784  MidNumAtLoc++;
5785  }
5786  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5787  {
5788  MidNumAtLoc++;
5789  }
5790  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5791  {
5792  MidNumAtLoc++;
5793  }
5794  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5795  {
5796  MidNumAtLoc++;
5797  }
5798  if(LeadNumAtLoc > MidNumAtLoc)
5799  // change back, else keep new values
5800  {
5801  FirstNamedElementPos = LeadPosA;
5802  FirstNamedLinkedElementPos = LeadPosB;
5803  SecondNamedElementPos = LeadPosC;
5804  SecondNamedLinkedElementPos = LeadPosD;
5805  }
5806  }
5807  }
5808  }
5809  }
5810  else
5811  {
5812  Utilities->CallLogPop(1791);
5813  return;
5814  }
5815  }
5816  else
5817  {
5818  throw Exception("Error - LocationName not set in FrontTrainSplit");
5819  }
5820  // set front & rear train parameters
5821  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5822  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5823  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5824  if(LeadElement == FirstNamedElementPos)
5825  {
5826  if(MidElement == SecondNamedElementPos)
5827  {
5828  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5829  FrontTrainRearPosition = FirstNamedElementPos;
5830  RearTrainFrontPosition = SecondNamedElementPos;
5831  RearTrainRearPosition = SecondNamedLinkedElementPos;
5832  }
5833  else // MidElement must == FirstNamedLinkedElementPos
5834  {
5835  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5836  FrontTrainRearPosition = SecondNamedElementPos;
5837  RearTrainFrontPosition = FirstNamedElementPos;
5838  RearTrainRearPosition = FirstNamedLinkedElementPos;
5839  }
5840  }
5841  else // MidElement == FirstNamedElementPos
5842  {
5843  if(LeadElement == SecondNamedElementPos)
5844  {
5845  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5846  FrontTrainRearPosition = SecondNamedElementPos;
5847  RearTrainFrontPosition = FirstNamedElementPos;
5848  RearTrainRearPosition = FirstNamedLinkedElementPos;
5849  }
5850  else // LeadElement must == FirstNamedLinkedElementPos
5851  {
5852  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5853  FrontTrainRearPosition = FirstNamedElementPos;
5854  RearTrainFrontPosition = SecondNamedElementPos;
5855  RearTrainRearPosition = SecondNamedLinkedElementPos;
5856  }
5857  }
5858  RearTrainExitPos = -1;
5859  for(int x = 0; x < 4; x++)
5860  {
5861  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5862  {
5863  RearTrainExitPos = x;
5864  break;
5865  }
5866  }
5867  if(RearTrainExitPos == -1)
5868  {
5869  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5870  }
5871  FrontTrainExitPos = -1;
5872  for(int x = 0; x < 4; x++)
5873  {
5874  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5875  {
5876  FrontTrainExitPos = x;
5877  break;
5878  }
5879  }
5880  if(FrontTrainExitPos == -1)
5881  {
5882  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5883  }
5884  // check no train (apart from self) on any of the 4 elements & fail if so
5885  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5886  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5887 
5888  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5889  {
5890  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5891  }
5892  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5893  {
5894  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5895  }
5896  else
5897  {
5898  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5899  }
5900  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5901  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5902  // can't be a bridge
5903  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5904  // can't be a bridge
5905  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5906  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5907 
5908  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5909  {
5910  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5911  }
5912  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5913  {
5914  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5915  }
5916  else
5917  {
5918  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5919  }
5920  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5921  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5922  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5923  {
5925  {
5928  }
5929  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5930  Utilities->CallLogPop(1010);
5931  return;
5932  }
5934  {
5936  }
5937  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5938  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5939  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5940  // variable as it is needed for setting up the new train
5941  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5942 
5943  UnplotTrain(0);
5944  StartSpeed = 0;
5945  RearStartElement = RearTrainRearPosition;
5946  RearStartExitPos = RearTrainExitPos;
5947  StoppedAtLocation = true;
5948  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5949  {
5950  StoppedWithoutPower = true;
5951  }
5952  PlotStartPosition(3);
5957 
5958  Mass = Mass / 2;
5959  MaxBrakeRate = MaxBrakeRate / 2;
5960  PowerAtRail = PowerAtRail / 2;
5961  AValue = sqrt(2 * PowerAtRail / Mass);
5962  // shouldn't change but include in case not set earlier
5963 
5964  // create new front train
5965 /*
5966  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5967  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5968  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5969 */
5970  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5971  TActionEventType EventType = NoEvent;
5972 
5973  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5974  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5975  // false for SignallerControl
5976  {
5977  Utilities->CallLogPop(1721); // EventType not used here
5978  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5979  // another train, in which case a message will have been sent to the perf log, also might well clear later
5980  // when other train moves away
5981  return;
5982  }
5983  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5984  // see mods in UpdateTrain for v1.3.2
5985  TrainController->TrainAdded = true;
5986 
5987  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5988 
5989  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5990  TTOD.RunningEntry = Running;
5991  Utilities->CallLogPop(998);
5992 }
5993 
5994 // ---------------------------------------------------------------------------
5995 
5996 void TTrain::RearTrainSplit(int Caller)
5997 {
5998 /*
5999  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
6000  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
6001  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
6002  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
6003 */
6004  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
6005  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
6006  if(PowerAtRail < 1)
6007  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
6008  {
6010  {
6011  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
6012  }
6014  Utilities->CallLogPop(2138);
6015  return;
6016  }
6017  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
6018 
6019  if(LocationName == "")
6020  {
6021  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
6022  }
6023  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
6024  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
6025  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
6027 
6028  // determine all positions & exits
6029  if(LocationName != "")
6030  {
6031  // if message given only call at ~5 sec intervals
6033  {
6034  FirstNamedElementPos = LeadElement;
6035  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6036  SecondNamedLinkedElementPos))
6037  {
6038  FirstNamedElementPos = MidElement;
6039  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6040  SecondNamedLinkedElementPos))
6041  {
6043  {
6044  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
6046  }
6047  Utilities->CallLogPop(1013);
6048  return;
6049  }
6050  }
6051  else
6052  {
6053  // if first is possible then check if all 4 positions at location, and if not try the second
6054  int LeadPosA = FirstNamedElementPos;
6055  int LeadPosB = FirstNamedLinkedElementPos;
6056  int LeadPosC = SecondNamedElementPos;
6057  int LeadPosD = SecondNamedLinkedElementPos;
6058  // count number of positions that are at the location
6059  int LeadNumAtLoc = 0;
6060  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
6061  {
6062  LeadNumAtLoc++;
6063  }
6064  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
6065  {
6066  LeadNumAtLoc++;
6067  }
6068  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
6069  {
6070  LeadNumAtLoc++;
6071  }
6072  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
6073  {
6074  LeadNumAtLoc++;
6075  }
6076  if(LeadNumAtLoc < 4)
6077  {
6078  FirstNamedElementPos = MidElement;
6079  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
6080  SecondNamedLinkedElementPos)) // restore originals
6081  {
6082  FirstNamedElementPos = LeadPosA;
6083  FirstNamedLinkedElementPos = LeadPosB;
6084  SecondNamedElementPos = LeadPosC;
6085  SecondNamedLinkedElementPos = LeadPosD;
6086  }
6087  else // count number at location
6088  {
6089  int MidNumAtLoc = 0;
6090  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
6091  {
6092  MidNumAtLoc++;
6093  }
6094  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6095  {
6096  MidNumAtLoc++;
6097  }
6098  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6099  {
6100  MidNumAtLoc++;
6101  }
6102  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
6103  {
6104  MidNumAtLoc++;
6105  }
6106  if(LeadNumAtLoc > MidNumAtLoc)
6107  // change back, else keep new values
6108  {
6109  FirstNamedElementPos = LeadPosA;
6110  FirstNamedLinkedElementPos = LeadPosB;
6111  SecondNamedElementPos = LeadPosC;
6112  SecondNamedLinkedElementPos = LeadPosD;
6113  }
6114  }
6115  }
6116  }
6117  }
6118  else
6119  {
6120  Utilities->CallLogPop(1792);
6121  return;
6122  }
6123  }
6124  else
6125  {
6126  throw Exception("Error - LocationName not set in RearTrainSplit");
6127  }
6128  // set front & rear train parameters
6129  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
6130  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
6131  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
6132  if(LeadElement == FirstNamedElementPos)
6133  {
6134  if(MidElement == SecondNamedElementPos)
6135  {
6136  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
6137  FrontTrainRearPosition = FirstNamedElementPos;
6138  RearTrainFrontPosition = SecondNamedElementPos;
6139  RearTrainRearPosition = SecondNamedLinkedElementPos;
6140  }
6141  else // MidElement must == FirstNamedLinkedElementPos
6142  {
6143  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
6144  FrontTrainRearPosition = SecondNamedElementPos;
6145  RearTrainFrontPosition = FirstNamedElementPos;
6146  RearTrainRearPosition = FirstNamedLinkedElementPos;
6147  }
6148  }
6149  else // MidElement == FirstNamedElementPos
6150  {
6151  if(LeadElement == SecondNamedElementPos)
6152  {
6153  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
6154  FrontTrainRearPosition = SecondNamedElementPos;
6155  RearTrainFrontPosition = FirstNamedElementPos;
6156  RearTrainRearPosition = FirstNamedLinkedElementPos;
6157  }
6158  else // LeadElement must == FirstNamedLinkedElementPos
6159  {
6160  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
6161  FrontTrainRearPosition = FirstNamedElementPos;
6162  RearTrainFrontPosition = SecondNamedElementPos;
6163  RearTrainRearPosition = SecondNamedLinkedElementPos;
6164  }
6165  }
6166  RearTrainExitPos = -1;
6167  for(int x = 0; x < 4; x++)
6168  {
6169  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
6170  {
6171  RearTrainExitPos = x;
6172  break;
6173  }
6174  }
6175  if(RearTrainExitPos == -1)
6176  {
6177  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
6178  }
6179  FrontTrainExitPos = -1;
6180  for(int x = 0; x < 4; x++)
6181  {
6182  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
6183  {
6184  FrontTrainExitPos = x;
6185  break;
6186  }
6187  }
6188  if(FrontTrainExitPos == -1)
6189  {
6190  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
6191  }
6192  // check no train (apart from self) on any of the 4 elements & fail if so
6193  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
6194  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
6195 
6196  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
6197  {
6198  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
6199  }
6200  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
6201  {
6202  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
6203  }
6204  else
6205  {
6206  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
6207  }
6208  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
6209  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
6210  // can't be a bridge
6211  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
6212  // can't be a bridge
6213  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
6214  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
6215 
6216  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
6217  {
6218  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
6219  }
6220  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
6221  {
6222  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
6223  }
6224  else
6225  {
6226  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
6227  }
6228  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
6229  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
6230  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
6231  {
6233  {
6236  }
6237  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
6238  Utilities->CallLogPop(1014);
6239  return;
6240  }
6242  {
6244  }
6245  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
6246  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
6247  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
6248  // variable as it is needed for setting up the new train
6249  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
6250 
6251  UnplotTrain(1);
6252  StartSpeed = 0;
6253  RearStartElement = FrontTrainRearPosition;
6254  RearStartExitPos = FrontTrainExitPos;
6255  StoppedAtLocation = true;
6256  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
6257  {
6258  StoppedWithoutPower = true;
6259  }
6260  PlotStartPosition(4);
6265  Mass = Mass / 2;
6266  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6267  MaxBrakeRate = MaxBrakeRate / 2;
6268  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6269  PowerAtRail = PowerAtRail / 2;
6270  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6271  AValue = sqrt(2 * PowerAtRail / Mass);
6272  // shouldn't change but include in case not set earlier
6273 
6274  // create new rear train
6275 /*
6276  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
6277  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
6278  int RepeatNumber, int IncrementalMinutes)
6279 */
6280  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6281  TActionEventType EventType = NoEvent;
6282 
6283  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
6284  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6285  // false for SignallerControl
6286  {
6287  Utilities->CallLogPop(1722); // EventType not used here
6288  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6289  // another train, in which case a message will have been sent to the perf log, also might well clear later
6290  // when other train moves away
6291  return;
6292  }
6293  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6294  // see mods in UpdateTrain for v1.3.2
6295  TrainController->TrainAdded = true;
6296  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6297 
6298  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6299  TTOD.RunningEntry = Running;
6300  Utilities->CallLogPop(1015);
6301 }
6302 
6303 // ---------------------------------------------------------------------------
6304 
6305 void TTrain::FinishJoin(int Caller)
6306 {
6307  if(FinishJoinLogSent == false)
6308  {
6309  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6310  FinishJoinLogSent = true; // so don't keep logging this event
6311  // don't need to reset it to false after the event as the train is deleted
6312  }
6313  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6314  if(TrainFailed)
6315  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6316  {
6318  {
6319  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6320  }
6322  Utilities->CallLogPop(2139);
6323  return;
6324  }
6325  if(TrainGone)
6326  // this means that the train has already joined the other & is awaiting deletion by TrainController
6327  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6328  // on from jbo & TrainToJoinIsAdjacent returns false
6329  {
6330  Utilities->CallLogPop(1035);
6331  return;
6332  }
6333  TTrain *TrainToJoin;
6335 
6336  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6337  {
6339  {
6340  // PerfLogForm->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6343  }
6344  Utilities->CallLogPop(1030);
6345  return; // keep this here in case need to add code before final return
6346  }
6347  // no need to clear error report flag here, cleared in jbo function
6348  // No need to set TimetableFinished, done in jbo function
6349  Utilities->CallLogPop(1031);
6350 }
6351 
6352 // ---------------------------------------------------------------------------
6353 
6354 void TTrain::JoinedBy(int Caller)
6355 {
6356  if(TrainController->OpTimeToActUpdateCounter == 0) //added at v2.13.2. Use OpTimeToActUpdateCounter for convenience so only issue the event log
6357  //once every second rather than many times. Can't use an event logged flag because there may
6358  //be several trains that are to be joined by others
6359  {
6360  TrainController->LogEvent("" + AnsiString(Caller) + "," + HeadCode + ",Waiting to be joined");
6361  }
6362  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6363  if(PowerAtRail < 1)
6364  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6365  {
6367  {
6368  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6369  }
6371  Utilities->CallLogPop(2140);
6372  return;
6373  }
6374  TTrain *TrainToBeJoinedBy;
6376 
6377  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6378  {
6380  {
6381  // PerfLogForm->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6384  }
6385  LastActionDelayFlag = true;
6386  // need to update LastActionTime if this train first to arrive as need 30s after
6387  // both adjacent before the join
6388  Utilities->CallLogPop(1032);
6389  return;
6390  }
6391  // here when other train is adjacent
6393  {
6395  // need to update this as need 30s after both adjacent before the join
6396  LastActionDelayFlag = false;
6397  Utilities->CallLogPop(1033);
6398  return;
6399  }
6400  // here when other train is adjacent & 30 secs elapsed since both adjacent
6401 
6402  // set new values for mass etc
6403  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6404  {
6405  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6406  }
6407  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6408  double OwnBrakeForce = MaxBrakeRate * Mass;
6409  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6410 
6411  Mass += TrainToBeJoinedBy->Mass;
6412  MaxBrakeRate = CombinedBrakeRate;
6413  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6414  AValue = sqrt(2 * PowerAtRail / Mass);
6415 
6417  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6418  TrainToBeJoinedBy->TimetableFinished = true;
6419  TrainToBeJoinedBy->TrainGone = true;
6420  TrainController->LogEvent("" + AnsiString(Caller) + "," + HeadCode + ",Joined By," + FJOHeadCode); //added at v2.13.2 to provide more information
6421  // this will cause other train to be deleted
6422  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6426  Utilities->CallLogPop(1034);
6427 }
6428 
6429 // ---------------------------------------------------------------------------
6430 
6431 void TTrain::ChangeTrainDirection(int Caller, bool NoLogFlag)
6432 {
6433  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6434  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6435  if(PowerAtRail < 1)
6436  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6437  {
6439  {
6440  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6441  }
6442  ZeroPowerNoCDTMessage = true;
6443  Utilities->CallLogPop(2141);
6444  return;
6445  }
6446  TColor TempColour = BackgroundColour;
6447 
6448  UnplotTrain(2);
6451  StartSpeed = 0;
6452  StoppedAtLocation = true;
6453  PlotStartPosition(1);
6454  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6455  // plot same as was - should always be pale green
6456  if(!NoLogFlag)
6457  {
6460  }
6462 
6463  //now erase a stub route if there is one, added at v2.5.1
6464  //first element of route is now immediately behind the train (i.e. next to MidElement)
6465  if(MidEntryPos >= 0)
6466  {
6467  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6468  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6469  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6470  int RouteNumber = -1;
6471  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6472  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6473  {
6474  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6475  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
6476  //elements can continue to be removed from that route
6477  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6478  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6479  {
6480  bool FirstPass = true; //added at v2.8.0
6481  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6482  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
6483  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6484  int TVPos2 = PDE.GetTrackVectorPosition();
6485  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6486  {
6487  break;
6488  }
6489  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6491  {
6492  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6493  }
6494  else
6495  {
6496  break;
6497  }
6498  FirstPass = false;
6499  }
6500  AllRoutes->RebuildRailwayFlag = true;
6501  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6502  }
6503  }
6504  }
6505  Utilities->CallLogPop(1012);
6506 }
6507 
6508 // ---------------------------------------------------------------------------
6509 
6510 void TTrain::NewTrainService(int Caller, bool NoLogFlag) //, bool NoLogFlag added at v2.12.0 for new service tt skips
6511 // change to new train, give new service message
6512 //same RepeatNumber used for the new service
6513 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6514  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6515  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6516  if(PowerAtRail < 1)
6517  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6518  {
6520  {
6521  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6522  }
6524  Utilities->CallLogPop(2142);
6525  return;
6526  }
6528 
6529  if(!NoLogFlag)
6530  {
6532  }
6533  UnplotTrain(3);
6536  StartSpeed = 0;
6541  HeadCode = NewHeadCode;
6542  StoppedAtLocation = true;
6543  PlotStartPosition(5);
6545  // pale green
6548  TerminatedMessageSent = false;
6549  Utilities->CallLogPop(1022);
6550 }
6551 
6552 // ---------------------------------------------------------------------------
6553 
6554 void TTrain::RemainHere(int Caller)
6555 {
6556  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6557  if(RemainHereLogNotSent) // to prevent repeated logs
6558  {
6559  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6560  RemainHereLogNotSent = false;
6561  }
6563  {
6567  TerminatedMessageSent = true;
6568  }
6569  TimetableFinished = true;
6570  Utilities->CallLogPop(1023);
6571 }
6572 
6573 // ---------------------------------------------------------------------------
6574 
6575 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6576 /*
6577  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6578  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6579  except where an action is a departure, starting at the current value for the pointer
6580  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6581  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6582 */{
6583  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6584  {
6585  return; // if remove train that starts under signaller control no messages needed
6586 
6587  }
6588  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6589  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6590  if(IncNum > 0)
6591  {
6592  for(int x = 0; x < IncNum; x++)
6593  {
6594  if(x > 0)
6595  {
6596  Ptr++;
6597  }
6598  // arrival - no need to test for termination as this section only covers missed actions up to the
6599  // arrival point - may terminate later but that not missed
6600  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6601  {
6603  }
6604  // arrival & departure
6605  if(Ptr->FormatType == TimeTimeLoc)
6606  {
6608  }
6609  // departure
6610  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6611  {
6612  continue; // skip TimeLoc departures, message given for arrivals
6613  }
6614  // pass
6615  else if(Ptr->FormatType == PassTime)
6616  {
6618  }
6619  // split
6620  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6621  {
6623  }
6624  // jbo
6625  else if(Ptr->Command == "jbo")
6626  {
6628  }
6629  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6630  // be starts, finishes or cdt
6631  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6632  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6633  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6634  (Ptr->FormatType == Repeat))
6635  {
6636  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6637  }
6638  }
6639  }
6640  else
6641  {
6642  bool IncludeFER = false;
6643  if(IncNum == -1)
6644  {
6645  IncludeFER = true;
6646  }
6647  while(true) // finish commands & repeats break out of loop
6648  {
6649  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6650  if(!IncludeFER && (Ptr->Command == "Fer"))
6651  {
6652  break;
6653  }
6654  // Fer & included
6655  else if(IncludeFER && (Ptr->Command == "Fer"))
6656  {
6658  break;
6659  }
6660  // Repeat
6661  else if(Ptr->FormatType == Repeat)
6662  {
6663  break;
6664  }
6665  // Fjo
6666  else if(Ptr->Command == "Fjo")
6667  {
6669  break;
6670  }
6671  // Frh
6672  else if(Ptr->Command == "Frh")
6673  {
6675  {
6677  TerminatedMessageSent = true;
6678  }
6679  break;
6680  }
6681  // Frh-sh
6682  else if(Ptr->Command == "Frh-sh")
6683  {
6685  {
6687  TerminatedMessageSent = true;
6688  }
6689  break;
6690  }
6691  // Fns, F-nshs, Fns-sh
6692  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6693  {
6695  break;
6696  }
6697  // end of breakout actions
6698  // arrival
6699  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6700  {
6701  if(IsTrainTerminating(1))
6702  {
6704  TerminatedMessageSent = true;
6705  }
6706  else
6707  {
6709  }
6710  }
6711  // arrival & departure
6712  else if(Ptr->FormatType == TimeTimeLoc)
6713  {
6715  }
6716  // departure
6717  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6718  {
6719  Ptr++;
6720  continue; // skip TimeLoc departures, message given for arrivals
6721  }
6722  // pass
6723  else if(Ptr->FormatType == PassTime)
6724  {
6726  }
6727  // split
6728  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6729  {
6731  }
6732  // jbo
6733  else if(Ptr->Command == "jbo")
6734  {
6736  }
6737  // cdt
6738  else if(Ptr->Command == "cdt")
6739  {
6740 // TrainController->LogActionError(25, HeadCode, "", FailMissedChangeDirection, Ptr->LocationName); //commented out at v2.12.0 as cdts not counted as missed events
6741  }
6742  // Errors
6743  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6744  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6745  {
6746  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6747  }
6748  Ptr++;
6749  }
6750  TimetableFinished = true;
6751  }
6752  Utilities->CallLogPop(1021);
6753 }
6754 
6755 // ---------------------------------------------------------------------------
6756 
6757 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6758 // ensure same repeatnumber
6759 {
6760  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6762 
6763  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6764  {
6765  Utilities->CallLogPop(1024);
6766  return(false);
6767  }
6768  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6769  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6770  {
6771  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6772  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6773  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6774  {
6775  Utilities->CallLogPop(1025);
6776  return(true);
6777  }
6778  }
6779  Utilities->CallLogPop(1026);
6780  return(false);
6781 }
6782 
6783 // ---------------------------------------------------------------------------
6784 
6785 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6786 // ensure same repeatnumber
6787 {
6788  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6789  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6790 
6791  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6792  {
6793  Utilities->CallLogPop(1027);
6794  return(false);
6795  }
6796  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6797  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6798  {
6799  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6800  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6801  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6802  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6803  {
6804  Utilities->CallLogPop(1028);
6805  return(true);
6806  }
6807  }
6808  Utilities->CallLogPop(1029);
6809  return(false);
6810 }
6811 
6812 // ---------------------------------------------------------------------------
6813 
6814 void TTrain::NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6815 { //same RepeatNumber (i.e. 0) used for the new shuttle
6816 //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6817  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6818  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6819  if(PowerAtRail < 1)
6820  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6821  {
6823  {
6824  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6825  }
6827  Utilities->CallLogPop(2143);
6828  return;
6829  }
6830  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6831 
6832  if(!NoLogFlag)
6833  {
6835  }
6836  UnplotTrain(4);
6839  StartSpeed = 0;
6844  HeadCode = NewHeadCode;
6845  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6846  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6847  StoppedAtLocation = true;
6848  PlotStartPosition(6);
6850  // pale green
6853  TerminatedMessageSent = false;
6854  Utilities->CallLogPop(1078);
6855 }
6856 
6857 // ---------------------------------------------------------------------------
6858 
6859 void TTrain::RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6860 // need to check whether all repeats finished or not
6861 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6862  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6863  if(RemainHereLogNotSent) // to prevent repeated logs
6864  {
6865  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6866  RemainHereLogNotSent = false;
6867  }
6869  // finished all repeats
6870  {
6872  { //no need to suppress this LogAction because BecomeNewService won't be available in this case
6875  TerminatedMessageSent = true;
6876  // no need to clear message as no more actions
6877  }
6878  TimetableFinished = true;
6879  Utilities->CallLogPop(1080);
6880  return;
6881  }
6882  if(PowerAtRail < 1)
6883  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6884  {
6886  {
6887  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6888  }
6890  Utilities->CallLogPop(2144);
6891  return;
6892  }
6893  int TempRepeatNumber = RepeatNumber + 1;
6894  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6895  // until after LogAction or the wrong time will be used
6896  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6897 
6898  if(!NoLogFlag)
6899  {
6901  }
6902  RepeatNumber++;
6903  UnplotTrain(5);
6906  StartSpeed = 0;
6911  HeadCode = NewHeadCode;
6912  StoppedAtLocation = true;
6913  PlotStartPosition(7);
6915  // pale green
6918  TerminatedMessageSent = false;
6919  Utilities->CallLogPop(1079);
6920 }
6921 
6922 // ---------------------------------------------------------------------------
6923 
6924 void TTrain::RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips
6925 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6926  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6927  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6928  if(PowerAtRail < 1)
6929  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6930  {
6932  {
6933  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6934  }
6936  Utilities->CallLogPop(2145);
6937  return;
6938  }
6940  // finished all repeats
6941  {
6942  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6943  if(!NoLogFlag)
6944  {
6946  }
6947  RepeatNumber = 0;
6948  UnplotTrain(6);
6951  StartSpeed = 0;
6953  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6954  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6956  HeadCode = NewHeadCode;
6957  StoppedAtLocation = true;
6958  PlotStartPosition(9);
6962  TerminatedMessageSent = false;
6963  Utilities->CallLogPop(1081);
6964  return;
6965  }
6966  int TempRepeatNumber = RepeatNumber + 1;
6967  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6968  // until after LogAction or the wrong time will be used
6969  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6970 
6971  if(!NoLogFlag)
6972  {
6974  }
6975  RepeatNumber++;
6976  UnplotTrain(7);
6979  StartSpeed = 0;
6984  HeadCode = NewHeadCode;
6985  StoppedAtLocation = true;
6986  PlotStartPosition(8);
6988  // pale green
6991  TerminatedMessageSent = false;
6992  Utilities->CallLogPop(1082);
6993 }
6994 
6995 // ---------------------------------------------------------------------------
6996 
6998 {
6999  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
7000  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
7001  // must be preceded by a TimeLoc departure
7002  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
7003  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
7004  {
7006  {
7007  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
7008  {
7009  Utilities->CallLogPop(1083);
7010  return(false);
7011  }
7012  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
7013  {
7014  Utilities->CallLogPop(1084);
7015  return(true);
7016  }
7017  }
7018  }
7019  Utilities->CallLogPop(1085);
7020  return(false);
7021 }
7022 
7023 // ---------------------------------------------------------------------------
7024 
7025 bool TTrain::AbleToMove(int Caller)
7026 {
7027  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
7028  bool Able = true;
7029 
7030  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0
7031  {
7032  // StoppedForTrainInFront removed as tested below
7033  Utilities->CallLogPop(2146); // added v2.4.0
7034  return(false); // added v2.4.0
7035  }
7036  if(LeadElement > -1)
7037  {
7038  if(Track->TrackElementAt(801, LeadElement).TrackType == Buffers) //moved up here from 'else' below at v2.12.0
7039  {
7040  StoppedForTrainInFront = false;
7041  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
7042  Utilities->CallLogPop(2456);
7043  return(false);
7044  }
7045  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
7046  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
7047  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront) //check if train in front still there
7048  {
7049  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
7050  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
7051  {
7052  Able = true;
7053  StoppedForTrainInFront = false;
7054  }
7055  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 == -1))
7056  {
7057  Able = true;
7058  StoppedForTrainInFront = false;
7059  }
7060  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 == -1))
7061  {
7062  Able = true;
7063  StoppedForTrainInFront = false;
7064  }
7065  else
7066  {
7067  Able = false; //added at v2.12.0 as train still in front so don't want signaller popup options to move available
7068  }
7069  }
7070  }
7071  else // leaving at a continuation so keep going
7072  {
7073  Able = true;
7074  StoppedForTrainInFront = false;
7075  }
7076  Utilities->CallLogPop(1454);
7077  return(Able);
7078 }
7079 
7080 // ---------------------------------------------------------------------------
7081 
7083 {
7084  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
7085  // won't be set; if there is a train then set StoppedForTrainInFront
7086  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
7087  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
7088  if(LeadElement == -1) // exiting at continuation
7089  {
7090  Utilities->CallLogPop(2045);
7091  return(false);
7092  }
7093  // end of addition
7094  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
7095  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
7096 
7097  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
7098  {
7099  StoppedForTrainInFront = true;
7100  Utilities->CallLogPop(1455);
7101  return(false);
7102  }
7103  else
7104  {
7105  Utilities->CallLogPop(1456);
7107  // StoppedWithoutPower added v2.4.0
7108  }
7109 }
7110 
7111 // ---------------------------------------------------------------------------
7112 
7114 {
7115  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
7116  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
7117  TColor TempColour = BackgroundColour;
7118 
7119  UnplotTrain(8);
7122  StartSpeed = 0;
7123  PlotStartPosition(2);
7124  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
7125 
7126  //now erase a stub route if there is one, added at v2.5.1
7127  //first element of route is now immediately behind the train (i.e. next to MidElement)
7128  if(MidEntryPos >= 0)
7129  {
7130  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
7131  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
7132  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
7133  int RouteNumber = -1;
7134  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
7135  if(RouteType == TAllRoutes::NotAutoSigsRoute)
7136  {
7137  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
7138  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
7139  //elements can continue to be removed from that route
7140  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
7141  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
7142  {
7143  bool FirstPass = true; //added at v2.8.0
7144  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
7145  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
7146  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
7147  int TVPos2 = PDE.GetTrackVectorPosition();
7148  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
7149  {
7150  break;
7151  }
7152  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
7154  {
7155  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
7156  }
7157  else
7158  {
7159  break;
7160  }
7161  FirstPass = false;
7162  }
7163  AllRoutes->RebuildRailwayFlag = true;
7164  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
7165  }
7166  }
7167  }
7168  Utilities->CallLogPop(1102);
7169 }
7170 
7171 // ---------------------------------------------------------------------------
7172 
7174 {
7175  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7176  ",FloatingLabelNextString" + "," + HeadCode);
7177  AnsiString RetStr = "", LocationName = "";
7178  //record action time - may be arrival, departure or event for use later (added at v2.13.2)
7179  TDateTime ActionTime = Ptr->ArrivalTime;
7180  if(ActionTime == TDateTime(-1))
7181  {
7182  ActionTime = Ptr->DepartureTime;
7183  }
7184  if(ActionTime == TDateTime(-1))
7185  {
7186  ActionTime = Ptr->EventTime;
7187  }
7188  //If ActionTime still TDateTime(-1) then the train has terminated and 'None...' will be returned
7189  //Now correct it for repeats
7190  if(ActionTime != TDateTime(-1))
7191  {
7192  ActionTime = GetTrainTime(64, ActionTime);
7193  }
7194  if(int(DelayedRandMins) > 0)
7195  {
7196  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7197  {
7198  throw Exception("Error - start entry in FloatingLabelNextString");
7199  }
7200  if(Ptr->FormatType == TimeTimeLoc)
7201  {
7202  if(TrainMode == Timetable)
7203  {
7204  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
7205  // not arrived yet in tt mode
7206  {
7207  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7208  }
7209  else
7210  {
7211  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7212  }
7213  }
7214  else // TrainMode == Signaller
7215  {
7216  if(!DepartureTimeSet) // not arrived yet
7217  {
7218  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7219  }
7220  else
7221  {
7222  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7223  }
7224  }
7225  }
7226  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7227  {
7228  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7229  }
7230  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7231  {
7232  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7233  }
7234  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7235  {
7236  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(46, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7237  }
7238  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7239  {
7240  RetStr = "Pass " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7241  }
7242  else if(Ptr->Command == "Fns")
7243  {
7244  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7245  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7246  RetStr = GetNewServiceDepartureInfo(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7247  }
7248  else if(Ptr->Command == "F-nshs")
7249  {
7250  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7251  Utilities->Format96HHMM(GetTrainTime(32, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7252  RetStr = GetNewServiceDepartureInfo(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7253  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7254  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7255  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7256  }
7257  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7258  {
7259  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7260  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7261  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7262  RetStr = GetNewServiceDepartureInfo(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7263  }
7264  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7265  {
7266  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7267  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7268  RetStr = GetNewServiceDepartureInfo(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7269  }
7270  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7271  {
7272  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7273  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7274  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7275  RetStr = GetNewServiceDepartureInfo(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7276  }
7277  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7278  {
7279  RetStr ="None, train terminated at " + Ptr->LocationName;
7280  }
7281  else if(Ptr->Command == "Frh")
7282  {
7283  RetStr = "None, train terminated at " + Ptr->LocationName;
7284  }
7285  else if(Ptr->Command == "Fer")
7286  {
7287  AnsiString AllowedExits = "";
7288  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at approx. " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime + TDateTime(DelayedRandMins/1440))) + AllowedExits;
7289  }
7290  else if(Ptr->Command == "Fjo")
7291  {
7292  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at approx. " +
7293  Utilities->Format96HHMM(GetTrainTime(11, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7294  }
7295  else if(Ptr->Command == "jbo")
7296  {
7297  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7298  " at approx. " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7299  }
7300  else if(Ptr->Command == "fsp")
7301  {
7302  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7303  " at approx. " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7304  }
7305  else if(Ptr->Command == "rsp")
7306  {
7307  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7308  " at approx. " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7309  }
7310  else if(Ptr->Command == "cdt")
7311  {
7312  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7313  }
7314  }
7315  else if(TrainController->TTClockTime > ActionTime) //condition added at v2.13.2 for trains that are delayed other than suffering a random delay
7316  {
7317  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7318  {
7319  throw Exception("Error - start entry in FloatingLabelNextString where TTClockTime > ActionTime");
7320  }
7321  if(Ptr->FormatType == TimeTimeLoc)
7322  {
7323  if(TrainMode == Timetable)
7324  {
7325  if(!TrainAtLocation(4, LocationName) || (LocationName != Ptr->LocationName))
7326  // not arrived yet in tt mode
7327  {
7328  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7329  }
7330  else
7331  {
7332  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7333  }
7334  }
7335  else // TrainMode == Signaller
7336  {
7337  if(!DepartureTimeSet) // not arrived yet
7338  {
7339  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7340  }
7341  else
7342  {
7343  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7344  }
7345  }
7346  }
7347  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7348  {
7349  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7350  }
7351  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7352  {
7353  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7354  }
7355  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7356  {
7357  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7358  }
7359  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7360  {
7361  RetStr = "Pass " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7362  }
7363  else if(Ptr->Command == "Fns")
7364  {
7365  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(60, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7366  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7367  RetStr = GetNewServiceDepartureInfo(19, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7368  }
7369  else if(Ptr->Command == "F-nshs")
7370  {
7371  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7373  RetStr = GetNewServiceDepartureInfo(20, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7374  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7375  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7376  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7377  }
7378  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7379  {
7380  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(61, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7381  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7382  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7383  RetStr = GetNewServiceDepartureInfo(21, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7384  }
7385  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7386  {
7387  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7388  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7389  RetStr = GetNewServiceDepartureInfo(22, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7390  }
7391  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7392  {
7393  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(62, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7394  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7395  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7396  RetStr = GetNewServiceDepartureInfo(23, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7397  }
7398  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7399  {
7400  RetStr ="None, train terminated at " + Ptr->LocationName;
7401  }
7402  else if(Ptr->Command == "Frh")
7403  {
7404  RetStr = "None, train terminated at " + Ptr->LocationName;
7405  }
7406  else if(Ptr->Command == "Fer")
7407  {
7408  AnsiString AllowedExits = "";
7409  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(5, Ptr->ExitList, AllowedExits) /*+ " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime)*/ + AllowedExits;
7410  }
7411  else if(Ptr->Command == "Fjo")
7412  {
7413  RetStr = "Join " + TrainController->GetRepeatHeadCode(63, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// + " at approx. " +
7414 // Utilities->Format96HHMM(TrainController->TTClockTime);
7415  }
7416  else if(Ptr->Command == "jbo")
7417  {
7418  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(64, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// +
7419 // " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7420  }
7421  else if(Ptr->Command == "fsp")
7422  {
7423  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(65, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7424  " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7425  }
7426  else if(Ptr->Command == "rsp")
7427  {
7428  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(66, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7429  " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7430  }
7431  else if(Ptr->Command == "cdt")
7432  {
7433  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7434  }
7435  }
7436  else //train not delayed
7437  {
7438  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7439  {
7440  throw Exception("Error - start entry in FloatingLabelNextString in final 'else'");
7441  }
7442  if(Ptr->FormatType == TimeTimeLoc)
7443  {
7444  if(TrainMode == Timetable)
7445  {
7446  if(!TrainAtLocation(3, LocationName) || (LocationName != Ptr->LocationName))
7447  // not arrived yet in tt mode
7448  {
7449  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(48, Ptr->ArrivalTime));
7450  }
7451  else
7452  {
7453  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7454  }
7455  }
7456  else // TrainMode == Signaller
7457  {
7458  if(!DepartureTimeSet) // not arrived yet
7459  {
7460  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(49, Ptr->ArrivalTime));
7461  }
7462  else
7463  {
7464  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7465  }
7466  }
7467  }
7468  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7469  {
7470  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(50, Ptr->ArrivalTime));
7471  }
7472  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7473  {
7474  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7475  }
7476  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7477  {
7478  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(51, Ptr->EventTime));
7479  }
7480  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7481  {
7482  RetStr = "Pass " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(52, Ptr->EventTime));
7483  }
7484  else if(Ptr->Command == "Fns")
7485  {
7486  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(53, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7487  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(53, Ptr->EventTime));
7488  RetStr = GetNewServiceDepartureInfo(10, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7489  }
7490  else if(Ptr->Command == "F-nshs")
7491  {
7492  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7494  RetStr = GetNewServiceDepartureInfo(12, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7495  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7496  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7497  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7498  }
7499  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7500  {
7501  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(54, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7502  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(55, Ptr->EventTime));
7503  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7504  RetStr = GetNewServiceDepartureInfo(14, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7505  }
7506  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7507  {
7508  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7509  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(56, Ptr->EventTime));
7510  RetStr = GetNewServiceDepartureInfo(16, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7511  }
7512  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7513  {
7514  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(55, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7515  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(57, Ptr->EventTime));
7516  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7517  RetStr = GetNewServiceDepartureInfo(18, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7518  }
7519  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7520  {
7521  RetStr ="None, train terminated at " + Ptr->LocationName;
7522  }
7523  else if(Ptr->Command == "Frh")
7524  {
7525  RetStr = "None, train terminated at " + Ptr->LocationName;
7526  }
7527  else if(Ptr->Command == "Fer")
7528  {
7529  AnsiString AllowedExits = "";
7530  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(4, Ptr->ExitList, AllowedExits) /*+ " at approx. " + Utilities->Format96HHMM(GetTrainTime(62, Ptr->EventTime))*/ + AllowedExits;
7531  }
7532  else if(Ptr->Command == "Fjo")
7533  {
7534  RetStr = "Join " + TrainController->GetRepeatHeadCode(56, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// + " at approx. " +
7535 // Utilities->Format96HHMM(GetTrainTime(58, Ptr->EventTime));
7536  }
7537  else if(Ptr->Command == "jbo")
7538  {
7539  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(57, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// +
7540 // " at approx. " + Utilities->Format96HHMM(GetTrainTime(59, Ptr->EventTime));
7541  }
7542  else if(Ptr->Command == "fsp")
7543  {
7544  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(58, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7545  " at approx. " + Utilities->Format96HHMM(GetTrainTime(60, Ptr->EventTime));
7546  }
7547  else if(Ptr->Command == "rsp")
7548  {
7549  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(59, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7550  " at approx. " + Utilities->Format96HHMM(GetTrainTime(61, Ptr->EventTime));
7551  }
7552  else if(Ptr->Command == "cdt")
7553  {
7554  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(63, Ptr->EventTime));
7555  }
7556  }
7557  Utilities->CallLogPop(1124);
7558  return(RetStr);
7559 }
7560 
7561 // ---------------------------------------------------------------------------
7562 /* as was
7563 AnsiString TTrain::FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
7564 {
7565  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7566  ",FloatingLabelNextString" + "," + HeadCode);
7567  AnsiString RetStr = "", LocationName = "";
7568 
7569  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7570  {
7571  throw Exception("Error - start entry in FloatingLabelNextString");
7572  }
7573  if(Ptr->FormatType == TimeTimeLoc)
7574  {
7575  if(TrainMode == Timetable)
7576  {
7577  if(!TrainAtLocation(, LocationName) || (LocationName != Ptr->LocationName))
7578  // not arrived yet in tt mode
7579  {
7580  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7581  }
7582  else
7583  {
7584  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7585  }
7586  }
7587  else // TrainMode == Signaller
7588  {
7589  if(!DepartureTimeSet) // not arrived yet
7590  {
7591  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7592  }
7593  else
7594  {
7595  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7596  }
7597  }
7598  }
7599  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7600  {
7601  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7602  }
7603  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7604  {
7605  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7606  }
7607  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7608  {
7609  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7610  }
7611  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7612  {
7613  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7614  }
7615  else if(Ptr->Command == "Fns")
7616  {
7617  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7618  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7619  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7620  }
7621  else if(Ptr->Command == "F-nshs")
7622  {
7623  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
7624  Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7625  RetStr = GetNewServiceDepartureInfo(, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7626  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7627  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7628  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7629  }
7630  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7631  {
7632  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7633  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7634  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7635  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7636  }
7637  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7638  {
7639  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7640  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7641  RetStr = GetNewServiceDepartureInfo(, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7642  }
7643  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7644  {
7645  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7646  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7647  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7648  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7649  }
7650  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7651  {
7652  RetStr ="None, train terminated at " + Ptr->LocationName;
7653  }
7654  else if(Ptr->Command == "Frh")
7655  {
7656  RetStr = "None, train terminated at " + Ptr->LocationName;
7657  }
7658  else if(Ptr->Command == "Fer")
7659  {
7660  AnsiString AllowedExits = "";
7661  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime)) + AllowedExits;
7662  }
7663  else if(Ptr->Command == "Fjo")
7664  {
7665  RetStr = "Join " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
7666  Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7667  }
7668  else if(Ptr->Command == "jbo")
7669  {
7670  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7671  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7672  }
7673  else if(Ptr->Command == "fsp")
7674  {
7675  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7676  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7677  }
7678  else if(Ptr->Command == "rsp")
7679  {
7680  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7681  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7682  }
7683  else if(Ptr->Command == "cdt")
7684  {
7685  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7686  }
7687  Utilities->CallLogPop();
7688  return(RetStr);
7689 }
7690 */
7691 // ---------------------------------------------------------------------------
7692 
7693 AnsiString TTrain::GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr, bool TimetableTime)
7694 { //last bool added at v2.13.2 so departure info adds random delay if actual rather than not timetable time required
7695  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
7696  + AnsiString(RptNum) + ",GetNewServiceDepartureInfo," + HeadCode);
7697  AnsiString DepTime = "", EventTime = "";
7698  bool CDTFlag = false; //reports if train changes direction before departs
7699  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
7700  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
7701  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
7702  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
7703  {
7704  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
7705  {
7706  TowardsLocation = AVI->LocationName;
7707  }
7708  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
7709  {
7710  TTrackElement TE = Track->TrackElementAt(1452, (AVI->ExitList.front()));
7711  if(TE.ActiveTrackElementName != "")
7712  {
7713  TowardsLocation = TE.ActiveTrackElementName;
7714  }
7715  else
7716  {
7717  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
7718  }
7719  }
7720  }
7721 
7722  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
7723  {
7724  if(AVI->Command == "cdt")
7725  {
7726  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
7727  continue;
7728  }
7729  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
7730  {
7731  TDateTime TTTime = TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes);
7732  if((DelayedRandMins >= 1) && !TimetableTime)
7733  {
7734  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7735  }
7736  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7737  {
7739  }
7740  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7741  {
7742  EventTime = Utilities->Format96HHMM(TTTime);
7743  }
7744  RetStr += "\nNew service splits at approx. " + EventTime;
7745  Utilities->CallLogPop(2234);
7746  return(RetStr);
7747  }
7748  if(AVI->Command == "jbo")
7749  {
7750  TDateTime TTTime = TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes);
7751  if((DelayedRandMins >= 1) && !TimetableTime)
7752  {
7753  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7754  }
7755  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7756  {
7758  }
7759  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7760  {
7761  EventTime = Utilities->Format96HHMM(TTTime);
7762  }
7763  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at approx. " + EventTime;
7764  Utilities->CallLogPop(2235);
7765  return(RetStr);
7766  }
7767  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
7768  {
7769  if(TimetableTime) //don't add random delay
7770  {
7771  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
7772  if(CDTFlag)
7773  {
7774  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7775  {
7776  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
7777  }
7778  else
7779  {
7780  RetStr += "\nNew service changes direction then departs at " + DepTime;
7781  }
7782  }
7783  else
7784  {
7785  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7786  {
7787  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
7788  }
7789  else
7790  {
7791  RetStr += "\nNew service departs at " + DepTime;
7792  }
7793  }
7794  }
7795  else if(DelayedRandMins >= 1)//add random delay
7796  {
7797  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(24, AVI->DepartureTime + TDateTime(DelayedRandMins/1440), RptNum, IncrementalMinutes));
7798  if(CDTFlag)
7799  {
7800  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7801  {
7802  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at approx. " + DepTime;
7803  }
7804  else
7805  {
7806  RetStr += "\nNew service changes direction then departs at approx. " + DepTime;
7807  }
7808  }
7809  else
7810  {
7811  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7812  {
7813  RetStr += "\nNew service departs towards " + TowardsLocation + " at approx. " + DepTime;
7814  }
7815  else
7816  {
7817  RetStr += "\nNew service departs at approx. " + DepTime;
7818  }
7819  }
7820  }
7821  else //no random delay but may be delayed for other reasons
7822  {
7823  TDateTime TTTime = TrainController->GetControllerTrainTime(25, AVI->DepartureTime, RptNum, IncrementalMinutes);
7824  if(TrainController->TTClockTime > TTTime)
7825  {
7827  }
7828  else
7829  {
7830  DepTime = Utilities->Format96HHMM(TTTime);
7831  }
7832  if(CDTFlag)
7833  {
7834  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7835  {
7836  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at approx. " + DepTime;
7837  }
7838  else
7839  {
7840  RetStr += "\nNew service changes direction then departs at approx. " + DepTime;
7841  }
7842  }
7843  else
7844  {
7845  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7846  {
7847  RetStr += "\nNew service departs towards " + TowardsLocation + " at approx. " + DepTime;
7848  }
7849  else
7850  {
7851  RetStr += "\nNew service departs at approx. " + DepTime;
7852  }
7853  }
7854  }
7855  Utilities->CallLogPop(2236);
7856  return(RetStr);
7857  }
7858  }
7859  Utilities->CallLogPop(2208);
7860  return(RetStr); //if reach here then RetStr doesn't change
7861 }
7862 
7863 // ---------------------------------------------------------------------------
7864 
7866 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7867 // If there are actions to be skipped but a departure is awaited (SkippedDeparture = true) then after the departure Ptr moves forward by SkipPtrValue
7868 {
7869  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7870  ",FloatingTimetableString" + "," + HeadCode);
7871  AnsiString RetStr = "", PartStr = "";
7872  int Count = 0;
7873  bool SkipDep = false, SkipDepActedOn = false; //SkipDepActedOn ensures only one SkipDep acted on
7874  AnsiString LocName = Ptr->LocationName;
7875 
7876  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7877  // can start in signaller control so exclude this
7878  {
7879  throw Exception("Error - start entry in FloatingTimetableString");
7880  }
7881  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTimeLoc check later
7882  bool FirstPass = true;
7883  Ptr--; // because incremented at start of loop
7884 
7885  // different first TimeTimeLoc display if in signaller control
7886  do
7887  {
7888  Ptr++;
7889  if((Ptr->FormatType == Repeat) || TimetableFinished)
7890  {
7891  break;
7892  }
7893  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7894  {
7895  AnsiString TrainLoc = "";
7896  if(TrainMode == Timetable)
7897  {
7898  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7899  {
7900  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7901  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7902  {
7903  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7904  }
7905  }
7906  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7907  {
7908  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7909  }
7910  else
7911  {
7912  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7913  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7914  Count++; // because there are 2 entries
7915  }
7916  }
7917  else // TrainMode == Signaller
7918  {
7919  if(DepartureTimeSet)
7920  {
7921  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7922  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7923  {
7924  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7925  }
7926  }
7927  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7928  {
7929  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7930  }
7931  else
7932  {
7933  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7934  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7935  Count++; // because there are 2 entries
7936  }
7937  }
7938  }
7939  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7940  {
7941  AnsiString TrainLoc = "";
7942  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7943  {
7944  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7945  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7946  {
7947  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7948  }
7949  }
7950  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7951  {
7952  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7953  }
7954  else
7955  {
7956  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7957  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7958  Count++; // because there are 2 entries
7959  }
7960  }
7961  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7962  {
7963  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7964  }
7965  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7966  {
7967  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7968  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7969  {
7970  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7971  }
7972  }
7973  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewService)
7974  { //note that TreatPassAsTimeLocDeparture can't be set if have SkippedDeparture
7975  PartStr = Utilities->Format96HHMM(GetTrainTime(47, Ptr->EventTime)) + ": Depart from " + Ptr->LocationName;
7976  }
7977  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7978  {
7979  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7980  }
7981  else if(Ptr->Command == "Fns")
7982  {
7983  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7984  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7985  PartStr = GetNewServiceDepartureInfo(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to PartStr
7986  }
7987  else if(Ptr->Command == "F-nshs")
7988  {
7989  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7990  Ptr->LocationName;
7991  PartStr = GetNewServiceDepartureInfo(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
7992  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7993  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7994  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7995  }
7996  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7997  {
7998  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7999  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
8000  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
8001  PartStr = GetNewServiceDepartureInfo(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
8002  }
8003  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
8004  {
8005  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
8006  +" at " + Ptr->LocationName;
8007  PartStr = GetNewServiceDepartureInfo(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
8008  }
8009  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
8010  {
8011  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
8012  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
8013  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
8014  PartStr = GetNewServiceDepartureInfo(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
8015  }
8016  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
8017  {
8018  PartStr = "Terminate at " + Ptr->LocationName;
8019  }
8020  else if(Ptr->Command == "Frh")
8021  {
8022  PartStr = "Terminate at " + Ptr->LocationName;
8023  }
8024  else if(Ptr->Command == "Fer")
8025  {
8026  AnsiString AllowedExits = "";
8027  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
8028  }
8029  else if(Ptr->Command == "Fjo")
8030  {
8031  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
8032  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8033  }
8034  else if(Ptr->Command == "jbo")
8035  {
8036  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
8037  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8038  }
8039  else if(Ptr->Command == "fsp")
8040  {
8041  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
8042  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8043  }
8044  else if(Ptr->Command == "rsp")
8045  {
8046  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
8047  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
8048  }
8049  else if(Ptr->Command == "cdt")
8050  {
8051  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
8052  }
8053  if(RetStr != "")
8054  {
8055  RetStr = RetStr + '\n' + PartStr;
8056  }
8057  else
8058  {
8059  RetStr = PartStr;
8060  }
8061  FirstPass = false;
8062  Count++;
8063 
8064  if(SkipDep)
8065  {
8066  Ptr = &(TrainDataEntryPtr->ActionVector.at(0)) + SkipPtrValue;
8067  Ptr--; //it is incremented at the start of the next loop
8068  SkipDep = false;
8069  SkipDepActedOn = true;
8070  }
8071  }
8072  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
8073  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
8074  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
8075  // forward as anyone should wish to see without looking at the full timetable
8076  if(TimetableFinished)
8077  {
8078  if(TrainMode == Timetable)
8079  {
8080  RetStr = "Timetable finished";
8081  }
8082  else
8083  {
8084  RetStr = "No timetable";
8085  }
8086  }
8087  Utilities->CallLogPop(1125);
8088  return("Timetable:\n" + RetStr);
8089 }
8090 
8091 // ---------------------------------------------------------------------------
8092 
8093 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
8094 {
8095  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
8096  Utilities->SaveFileString(OutFile, HeadCode);
8099  Utilities->SaveFileInt(OutFile, StartSpeed);
8102  Utilities->SaveFileInt(OutFile, RepeatNumber);
8105  Utilities->SaveFileInt(OutFile, Mass);
8108  Utilities->SaveFileDouble(OutFile, EntrySpeed);
8115  Utilities->SaveFileDouble(OutFile, BrakeRate);
8119  Utilities->SaveFileDouble(OutFile, double(EntryTime));
8120  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
8121  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
8122  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
8123  Utilities->SaveFileDouble(OutFile, double(TRSTime));
8124  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
8128  Utilities->SaveFileInt(OutFile, (short)TrainMode);
8133  Utilities->SaveFileBool(OutFile, Derailed);
8135  Utilities->SaveFileBool(OutFile, Crashed);
8142  Utilities->SaveFileBool(OutFile, NotInService);
8143  Utilities->SaveFileBool(OutFile, Plotted);
8144  Utilities->SaveFileBool(OutFile, TrainGone);
8145  Utilities->SaveFileBool(OutFile, SPADFlag);
8147  Utilities->SaveFileInt(OutFile, HOffset[0]);
8148  Utilities->SaveFileInt(OutFile, HOffset[1]);
8149  Utilities->SaveFileInt(OutFile, HOffset[2]);
8150  Utilities->SaveFileInt(OutFile, HOffset[3]);
8151  Utilities->SaveFileInt(OutFile, VOffset[0]);
8152  Utilities->SaveFileInt(OutFile, VOffset[1]);
8153  Utilities->SaveFileInt(OutFile, VOffset[2]);
8154  Utilities->SaveFileInt(OutFile, VOffset[3]);
8155  Utilities->SaveFileInt(OutFile, PlotElement[0]);
8156  Utilities->SaveFileInt(OutFile, PlotElement[1]);
8157  Utilities->SaveFileInt(OutFile, PlotElement[2]);
8158  Utilities->SaveFileInt(OutFile, PlotElement[3]);
8159  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
8160  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
8161  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
8162  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
8164  Utilities->SaveFileInt(OutFile, (short)Straddle);
8165  Utilities->SaveFileInt(OutFile, NextTrainID);
8166  Utilities->SaveFileInt(OutFile, TrainID);
8167  Utilities->SaveFileInt(OutFile, LeadElement);
8168  Utilities->SaveFileInt(OutFile, LeadEntryPos);
8169  Utilities->SaveFileInt(OutFile, LeadExitPos);
8170  Utilities->SaveFileInt(OutFile, MidElement);
8171  Utilities->SaveFileInt(OutFile, MidEntryPos);
8172  Utilities->SaveFileInt(OutFile, MidExitPos);
8173  Utilities->SaveFileInt(OutFile, LagElement);
8174  Utilities->SaveFileInt(OutFile, LagEntryPos);
8175  Utilities->SaveFileInt(OutFile, LagExitPos);
8176  int ColourNumber;
8177 
8179  {
8180  ColourNumber = 0;
8181  }
8183  {
8184  ColourNumber = 1;
8185  }
8187  {
8188  ColourNumber = 2;
8189  }
8191  {
8192  ColourNumber = 3;
8193  }
8195  {
8196  ColourNumber = 4;
8197  }
8199  {
8200  ColourNumber = 5;
8201  }
8203  {
8204  ColourNumber = 6;
8205  }
8207  {
8208  ColourNumber = 7;
8209  }
8211  {
8212  ColourNumber = 8;
8213  }
8215  {
8216  ColourNumber = 9;
8217  }
8219  {
8220  ColourNumber = 10;
8221  }
8223  {
8224  ColourNumber = 11;
8225  }
8227  {
8228  ColourNumber = 12;
8229  }
8230  else if(BackgroundColour == clTRSBackground)
8231  {
8232  ColourNumber = 13;
8233  }
8235  {
8236  ColourNumber = 14; // added at v2.4.0
8237  }
8238  Utilities->SaveFileInt(OutFile, ColourNumber);
8239 
8240  // additional data
8241  bool ForwardHeadCode;
8242 
8243  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
8244  {
8245  ForwardHeadCode = true;
8246  }
8247  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
8248  else
8249  {
8250  ForwardHeadCode = false;
8251  }
8252  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
8253 
8254  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
8255 
8256  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
8257  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
8258 
8259  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
8260  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
8261  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
8262  // so use the last asterisk position for this - 0 for false & 1 for true
8263  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
8264  AnsiString Marker;
8265 
8267  {
8268  Marker = "*****1";
8269  }
8270  else
8271  {
8272  Marker = "*****0";
8273  }
8274  if(RestoreTimetableLocation == "")
8275  {
8276  Utilities->SaveFileString(OutFile, Marker);
8277  }
8278  else
8279  {
8280  AnsiString CombinedString = RestoreTimetableLocation + Marker;
8281  Utilities->SaveFileString(OutFile, CombinedString);
8282  // RestoreTimetableLocation + marker
8283  }
8284  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
8285  Utilities->CallLogPop(1457);
8286 }
8287 
8288 // ---------------------------------------------------------------------------
8289 
8290 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
8291 {
8292  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
8293  HeadCode = Utilities->LoadFileString(InFile);
8296  StartSpeed = Utilities->LoadFileInt(InFile);
8298  if(SignallerMaxSpeed < 10)
8299  {
8300  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
8301  }
8303  RepeatNumber = Utilities->LoadFileInt(InFile);
8306  Mass = Utilities->LoadFileInt(InFile);
8309  {
8311  }
8312  // above added at v2.1.0 for legacy session files where value may not have been limited
8314  EntrySpeed = Utilities->LoadFileDouble(InFile);
8318  if(TimetableMaxRunningSpeed < 10)
8319  {
8320  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8321  }
8323  if(MaxRunningSpeed < 10)
8324  {
8325  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8326  }
8329  BrakeRate = Utilities->LoadFileDouble(InFile);
8333  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
8334  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
8335  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
8336  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
8337  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
8338  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
8347  Derailed = Utilities->LoadFileBool(InFile);
8349  Crashed = Utilities->LoadFileBool(InFile);
8356  NotInService = Utilities->LoadFileBool(InFile);
8357  Plotted = Utilities->LoadFileBool(InFile);
8358  TrainGone = Utilities->LoadFileBool(InFile);
8359  SPADFlag = Utilities->LoadFileBool(InFile);
8361  HOffset[0] = Utilities->LoadFileInt(InFile);
8362  HOffset[1] = Utilities->LoadFileInt(InFile);
8363  HOffset[2] = Utilities->LoadFileInt(InFile);
8364  HOffset[3] = Utilities->LoadFileInt(InFile);
8365  VOffset[0] = Utilities->LoadFileInt(InFile);
8366  VOffset[1] = Utilities->LoadFileInt(InFile);
8367  VOffset[2] = Utilities->LoadFileInt(InFile);
8368  VOffset[3] = Utilities->LoadFileInt(InFile);
8369  PlotElement[0] = Utilities->LoadFileInt(InFile);
8370  PlotElement[1] = Utilities->LoadFileInt(InFile);
8371  PlotElement[2] = Utilities->LoadFileInt(InFile);
8372  PlotElement[3] = Utilities->LoadFileInt(InFile);
8373  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
8374  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
8375  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
8376  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
8378  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
8379  NextTrainID = Utilities->LoadFileInt(InFile);
8380  // will be same for all but best to save all anyway
8381  TrainID = Utilities->LoadFileInt(InFile);
8382  LeadElement = Utilities->LoadFileInt(InFile);
8383  LeadEntryPos = Utilities->LoadFileInt(InFile);
8384  LeadExitPos = Utilities->LoadFileInt(InFile);
8385  MidElement = Utilities->LoadFileInt(InFile);
8386  MidEntryPos = Utilities->LoadFileInt(InFile);
8387  MidExitPos = Utilities->LoadFileInt(InFile);
8388  LagElement = Utilities->LoadFileInt(InFile);
8389  LagEntryPos = Utilities->LoadFileInt(InFile);
8390  LagExitPos = Utilities->LoadFileInt(InFile);
8391  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
8392 
8393  if(ColourNumber == 0)
8394  {
8396  }
8397  else if(ColourNumber == 1)
8398  {
8400  }
8401  else if(ColourNumber == 2)
8402  {
8404  }
8405  else if(ColourNumber == 3)
8406  {
8408  }
8409  else if(ColourNumber == 4)
8410  {
8412  }
8413  else if(ColourNumber == 5)
8414  {
8416  }
8417  else if(ColourNumber == 6)
8418  {
8420  }
8421  else if(ColourNumber == 7)
8422  {
8424  }
8425  else if(ColourNumber == 8)
8426  {
8428  }
8429  else if(ColourNumber == 9)
8430  {
8432  }
8433  else if(ColourNumber == 10)
8434  {
8436  }
8437  else if(ColourNumber == 11)
8438  {
8440  }
8441  else if(ColourNumber == 12)
8442  {
8444  }
8445  else if(ColourNumber == 13)
8446  {
8448  }
8449  else if(ColourNumber == 14)
8450  {
8451  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
8452 
8453  }
8454  // additional data
8456  // sets the BackgroundColour to the loaded value
8457  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
8458 
8459  if(ForwardHeadCode)
8460  {
8461  for(int x = 0; x < 4; x++)
8462  {
8464  }
8465  }
8466  else
8467  {
8468  for(int x = 0; x < 4; x++)
8469  {
8470  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
8471  }
8472  }
8473  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
8474  if(TrainMode == Timetable)
8475  {
8476  if(Crashed)
8477  {
8479  }
8480  else
8481  {
8483  }
8484  }
8485  else
8486  {
8488  }
8490  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
8491  if(Straddle == LeadMid)
8492  {
8493  if(LeadElement > -1)
8494  {
8496  }
8497  if(LeadElement > -1)
8498  {
8500  }
8501  if(MidElement > -1)
8502  {
8504  }
8505  if(MidElement > -1)
8506  {
8508  }
8509  }
8510  else if(Straddle == LeadMidLag)
8511  {
8512  if(LeadElement > -1)
8513  {
8515  }
8516  if(MidElement > -1)
8517  {
8519  }
8520  if(MidElement > -1)
8521  {
8523  }
8524  if(LagElement > -1)
8525  {
8527  }
8528  }
8529  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
8530 
8531  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
8532  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
8533 
8534  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
8535 
8536  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
8537  if(LeadElement > -1)
8538  // need to include this in case train exiting & no lead element
8539  {
8541  {
8542  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
8543  }
8544  }
8545  AValue = sqrt(2 * PowerAtRail / Mass);
8546 
8547  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
8548 
8549  // possible RestoreTimetableLocation + Marker, where Marker is
8550  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
8551  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
8552  // added at beta v0.2e
8553  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
8554  // name not allowed to include the '*' character
8555  {
8556  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
8557  bool GiveMessagesFalse = false;
8558  bool CheckLocationsExistInRailwayTrue = true;
8559  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
8560  {
8561  // otherwise take no action
8562  RestoreTimetableLocation = Location;
8563  }
8564  }
8565  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
8566 
8567  StoppedWithoutPower = false;
8568  if(Marker[6] == '1')
8569  {
8570  StoppedWithoutPower = true;
8571  }
8572  Utilities->CallLogPop(1458);
8573 }
8574 
8575 // ---------------------------------------------------------------------------
8576 
8577 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
8578 {
8580  {
8581  return(false); // HeadCode
8582 
8583  }
8584  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8585  {
8586  return(false); // RearStartElement
8587 
8588  }
8589  if(!Utilities->CheckFileInt(InFile, 0, 3))
8590  {
8591  return(false); // RearStartExitPos
8592 
8593  }
8594  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8595  {
8596  return(false); // StartSpeed
8597 
8598  }
8599  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8600  {
8601  return(false); // SignallerMaxSpeed
8602 
8603  }
8604  if(!Utilities->CheckFileBool(InFile))
8605  {
8606  return(false); // HoldAtLocationInTTMode
8607 
8608  }
8609  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8610  {
8611  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
8612 
8613  }
8614  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8615  {
8616  return(false); // IncrementalMinutes (max 96 x 60)
8617 
8618  }
8619  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8620  {
8621  return(false); // IncrementalDigits
8622 
8623  }
8624  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8625  {
8626  return(false); // Mass
8627 
8628  }
8629  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
8630  {
8631  return(false);
8632  }
8633  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
8634  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
8635  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8636  {
8637  return(false); // FrontElementLength
8638 
8639  }
8640  if(!Utilities->CheckFileDouble(InFile))
8641  {
8642  return(false); // EntrySpeed
8643 
8644  }
8645  if(!Utilities->CheckFileDouble(InFile))
8646  {
8647  return(false); // ExitSpeedHalf
8648 
8649  }
8650  if(!Utilities->CheckFileDouble(InFile))
8651  {
8652  return(false); // ExitSpeedFull
8653 
8654  }
8655  if(!Utilities->CheckFileDouble(InFile))
8656  {
8657  return(false); // TimetableMaxRunningSpeed
8658 
8659  }
8660  if(!Utilities->CheckFileDouble(InFile))
8661  {
8662  return(false); // MaxRunningSpeed
8663 
8664  }
8665  if(!Utilities->CheckFileDouble(InFile))
8666  {
8667  return(false); // MaxExitSpeed
8668 
8669  }
8670  if(!Utilities->CheckFileDouble(InFile))
8671  {
8672  return(false); // MaxBrakeRate
8673 
8674  }
8675  if(!Utilities->CheckFileDouble(InFile))
8676  {
8677  return(false); // BrakeRate
8678 
8679  }
8680  if(!Utilities->CheckFileDouble(InFile))
8681  {
8682  return(false); // PowerAtRail
8683 
8684  }
8685  if(!Utilities->CheckFileBool(InFile))
8686  {
8687  return(false); // FirstHalfMove
8688 
8689  }
8690  if(!Utilities->CheckFileBool(InFile))
8691  {
8692  return(false); // OneLengthAccelDecel
8693 
8694  }
8695  if(!Utilities->CheckFileDouble(InFile))
8696  {
8697  return(false); // double(EntryTime)
8698 
8699  }
8700  if(!Utilities->CheckFileDouble(InFile))
8701  {
8702  return(false); // double(ExitTimeHalf)
8703 
8704  }
8705  if(!Utilities->CheckFileDouble(InFile))
8706  {
8707  return(false); // double(ExitTimeFull)
8708 
8709  }
8710  if(!Utilities->CheckFileDouble(InFile))
8711  {
8712  return(false); // double(ReleaseTime)
8713 
8714  }
8715  if(!Utilities->CheckFileDouble(InFile))
8716  {
8717  return(false); // double(TRSTime)
8718 
8719  }
8720  if(!Utilities->CheckFileDouble(InFile))
8721  {
8722  return(false); // double(LastActionTime)
8723 
8724  }
8725  if(!Utilities->CheckFileBool(InFile))
8726  {
8727  return(false); // CallingOnFlag
8728 
8729  }
8730  if(!Utilities->CheckFileBool(InFile))
8731  {
8732  return(false); // BeingCalledOn
8733 
8734  }
8735  if(!Utilities->CheckFileBool(InFile))
8736  {
8737  return(false); // DepartureTimeSet
8738 
8739  }
8740  if(!Utilities->CheckFileInt(InFile, 0, 2))
8741  {
8742  return(false); // (short)TrainMode
8743 
8744  }
8745  if(!Utilities->CheckFileBool(InFile))
8746  {
8747  return(false); // TimetableFinished
8748 
8749  }
8750  if(!Utilities->CheckFileBool(InFile))
8751  {
8752  return(false); // LastActionDelayFlag
8753 
8754  }
8755  if(!Utilities->CheckFileBool(InFile))
8756  {
8757  return(false); // SignallerRemoved
8758 
8759  }
8760  if(!Utilities->CheckFileBool(InFile))
8761  {
8762  return(false); // TerminatedMessageSent
8763 
8764  }
8765  if(!Utilities->CheckFileBool(InFile))
8766  {
8767  return(false); // Derailed
8768 
8769  }
8770  if(!Utilities->CheckFileBool(InFile))
8771  {
8772  return(false); // DerailPending
8773 
8774  }
8775  if(!Utilities->CheckFileBool(InFile))
8776  {
8777  return(false); // Crashed
8778 
8779  }
8780  if(!Utilities->CheckFileBool(InFile))
8781  {
8782  return(false); // StoppedAtBuffers
8783 
8784  }
8785  if(!Utilities->CheckFileBool(InFile))
8786  {
8787  return(false); // StoppedAtSignal
8788 
8789  }
8790  if(!Utilities->CheckFileBool(InFile))
8791  {
8792  return(false); // StoppedAtLocation
8793 
8794  }
8795  if(!Utilities->CheckFileBool(InFile))
8796  {
8797  return(false); // SignallerStopped
8798 
8799  }
8800  if(!Utilities->CheckFileBool(InFile))
8801  {
8802  return(false); // StoppedAfterSPAD
8803 
8804  }
8805  if(!Utilities->CheckFileBool(InFile))
8806  {
8807  return(false); // StoppedForTrainInFront
8808 
8809  }
8810  if(!Utilities->CheckFileBool(InFile))
8811  {
8812  return(false); // NotInService
8813 
8814  }
8815  if(!Utilities->CheckFileBool(InFile))
8816  {
8817  return(false); // Plotted
8818 
8819  }
8820  if(!Utilities->CheckFileBool(InFile))
8821  {
8822  return(false); // TrainGone
8823 
8824  }
8825  if(!Utilities->CheckFileBool(InFile))
8826  {
8827  return(false); // SPADFlag
8828 
8829  }
8830  if(!Utilities->CheckFileBool(InFile))
8831  {
8832  return(false); // TimeTimeLocArrived
8833 
8834  }
8835  if(!Utilities->CheckFileInt(InFile, 0, 15))
8836  {
8837  return(false); // HOffset[0]
8838 
8839  }
8840  if(!Utilities->CheckFileInt(InFile, 0, 15))
8841  {
8842  return(false); // HOffset[1]
8843 
8844  }
8845  if(!Utilities->CheckFileInt(InFile, 0, 15))
8846  {
8847  return(false); // HOffset[2]
8848 
8849  }
8850  if(!Utilities->CheckFileInt(InFile, 0, 15))
8851  {
8852  return(false); // HOffset[3]
8853 
8854  }
8855  if(!Utilities->CheckFileInt(InFile, 0, 15))
8856  {
8857  return(false); // VOffset[0]
8858 
8859  }
8860  if(!Utilities->CheckFileInt(InFile, 0, 15))
8861  {
8862  return(false); // VOffset[1]
8863 
8864  }
8865  if(!Utilities->CheckFileInt(InFile, 0, 15))
8866  {
8867  return(false); // VOffset[2]
8868 
8869  }
8870  if(!Utilities->CheckFileInt(InFile, 0, 15))
8871  {
8872  return(false); // VOffset[3]
8873 
8874  }
8875  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8876  {
8877  return(false); // PlotElement[0]
8878 
8879  }
8880  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8881  {
8882  return(false); // PlotElement[1]
8883 
8884  }
8885  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8886  {
8887  return(false); // PlotElement[2]
8888 
8889  }
8890  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8891  {
8892  return(false); // PlotElement[3]
8893 
8894  }
8895  if(!Utilities->CheckFileInt(InFile, 0, 3))
8896  {
8897  return(false); // PlotEntryPos[0]
8898 
8899  }
8900  if(!Utilities->CheckFileInt(InFile, 0, 3))
8901  {
8902  return(false); // PlotEntryPos[1]
8903 
8904  }
8905  if(!Utilities->CheckFileInt(InFile, 0, 3))
8906  {
8907  return(false); // PlotEntryPos[2]
8908 
8909  }
8910  if(!Utilities->CheckFileInt(InFile, 0, 3))
8911  {
8912  return(false); // PlotEntryPos[3]
8913 
8914  }
8915  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8916  {
8917  return(false); // TrainCrashedInto
8918 
8919  }
8920  if(!Utilities->CheckFileInt(InFile, 0, 2))
8921  {
8922  return(false); // (short)Straddle
8923 
8924  }
8925  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8926  {
8927  return(false); // NextTrainID
8928 
8929  }
8930  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8931  {
8932  return(false); // TrainID
8933 
8934  }
8935  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8936  {
8937  return(false); // LeadElement
8938 
8939  }
8940  if(!Utilities->CheckFileInt(InFile, 0, 3))
8941  {
8942  return(false); // LeadEntryPos
8943 
8944  }
8945  if(!Utilities->CheckFileInt(InFile, 0, 3))
8946  {
8947  return(false); // LeadExitPos
8948 
8949  }
8950  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8951  {
8952  return(false); // MidElement
8953 
8954  }
8955  if(!Utilities->CheckFileInt(InFile, 0, 3))
8956  {
8957  return(false); // MidEntryPos
8958 
8959  }
8960  if(!Utilities->CheckFileInt(InFile, 0, 3))
8961  {
8962  return(false); // MidExitPos
8963 
8964  }
8965  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8966  {
8967  return(false); // LagElement
8968 
8969  }
8970  if(!Utilities->CheckFileInt(InFile, 0, 3))
8971  {
8972  return(false); // LagEntryPos
8973 
8974  }
8975  if(!Utilities->CheckFileInt(InFile, 0, 3))
8976  {
8977  return(false); // LagExitPos
8978 
8979  }
8980  if(!Utilities->CheckFileInt(InFile, 0, 14))
8981  {
8982  return(false);
8983  }
8984  // Background colour number //14 is failed colour at v2.4.0
8985  if(!Utilities->CheckFileBool(InFile))
8986  {
8987  return(false); // ForwardHeadCode
8988 
8989  }
8990  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8991  {
8992  return(false); // TrainDataEntryValue
8993 
8994  }
8995  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8996  {
8997  return(false); // ActionVectorEntryValue
8998 
8999  }
9001  {
9002  return(false); // End of train marker + possible RestoreTimetableLocation
9003 
9004  }
9005  // and StoppedWithoutPower flag
9006  return(true);
9007 }
9008 
9009 // ---------------------------------------------------------------------------
9010 
9011 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
9012 {
9013  // order below reflects significance so earlier shows first, as may have more than one flag set
9014  // only plot flashing trains when Flash is true
9015 
9016 /*
9017  clCrashedBackground (TColor)0x0000FF red
9018  clDerailedBackground (TColor)0x0000FF red
9019  clSPADBackground (TColor)0x00FFFF yellow
9020  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
9021  clCallOnBackground (TColor)0xFF33FF light magenta
9022  clSignalStopBackground (TColor)0x00FF66 green
9023  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
9024  clStationStopBackground (TColor)0xCCFFCC pale green
9025  clTRSBackground (TColor)0xFFCCFF light pink
9026  clBufferStopBackground (TColor)0xFFFFCC pale cyan
9027  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
9028  clSignallerStopped (TColor)0x99CCFF caramel
9029  clNormalBackground (TColor)0xCCCCCC grey
9030 */
9031 
9032  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
9033  bool HideFlashingTrain = true;
9034  // hide it when Flash false so it blinks on and off
9035  // if don't hide it it stays displayed all the time
9036  Graphics::TBitmap *SmallTrainBitmap;
9037 
9038  // NB ensure retain same order as zoomed in order so colours correspond
9040  {
9041  TrainController->CrashWarning = true;
9042  SmallTrainBitmap = RailGraphics->smRed;
9043  }
9045  {
9047  SmallTrainBitmap = RailGraphics->smRed;
9048  }
9050  {
9051  TrainController->SPADWarning = true;
9052  SmallTrainBitmap = RailGraphics->smYellow;
9053  }
9055  {
9057  SmallTrainBitmap = RailGraphics->smOrange;
9058  }
9060  {
9062  SmallTrainBitmap = RailGraphics->smMagenta;
9063  }
9065  {
9067  SmallTrainBitmap = RailGraphics->smBrightGreen;
9068  }
9070  {
9072  SmallTrainBitmap = RailGraphics->smCyan;
9073  }
9075  {
9076  SmallTrainBitmap = RailGraphics->smPaleGreen;
9077  HideFlashingTrain = false;
9078  }
9080  {
9081  SmallTrainBitmap = RailGraphics->smCyan;
9082  HideFlashingTrain = false;
9083  }
9085  {
9086  SmallTrainBitmap = RailGraphics->smLightBlue;
9087  HideFlashingTrain = false;
9088  }
9090  {
9091  SmallTrainBitmap = RailGraphics->smCaramel;
9092  HideFlashingTrain = false;
9093  }
9094  else
9095  {
9096  SmallTrainBitmap = RailGraphics->smBlack; // moving
9097  HideFlashingTrain = false;
9098  }
9099  // now plot the new train
9100  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
9101  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
9102  {
9103  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
9104  }
9105  if((MidElement > -1) && (!HideFlashingTrain || Flash))
9106  {
9107  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
9108  }
9109  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
9110  {
9111  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
9112  }
9116  Utilities->CallLogPop(1459);
9117 }
9118 
9119 // ---------------------------------------------------------------------------
9120 
9122 {
9123  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
9124  if(!Display->ZoomOutFlag)
9125  {
9126  Utilities->CallLogPop(1304);
9127  return;
9128  }
9129  for(int y = 0; y < 3; y++)
9130  {
9131  if(OldZoomOutElement[y] > -1)
9132  {
9133  bool FoundFlag = false;
9134  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
9135  TTrackElement IATElement1, IATElement2;
9136  // default elements to begin with
9137  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
9138  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
9139  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
9140  if(FoundFlag)
9141  {
9142  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
9143  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
9144  if(IMPair.first != IMPair.second)
9145  {
9146  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
9147  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
9148  }
9149  }
9150  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
9151  }
9152  }
9153  Utilities->CallLogPop(1305);
9154 }
9155 
9156 // ---------------------------------------------------------------------------
9157 
9158 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
9159 {
9160  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
9161  LocationName = "";
9162  if(!RevisedStoppedAtLoc())
9163  {
9164  Utilities->CallLogPop(1398);
9165  return(false);
9166  }
9167  if(LeadElement > -1)
9168  {
9170  }
9171  if((LocationName == "") && (MidElement > -1))
9172  {
9173  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
9174  }
9175  if((LocationName == "") && (LagElement > -1))
9176  {
9177  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
9178  }
9179  if(LocationName == "")
9180  {
9181  throw Exception("Error - Location name not set in TrainAtLocation");
9182  }
9183  Utilities->CallLogPop(1399);
9184  return(true);
9185 }
9186 
9187 // ---------------------------------------------------------------------------
9188 
9189 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
9190 {
9191  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
9192  for(int x = 0; x < 4; x++)
9193  {
9194  PlotTrainGraphic(7, x, Disp);
9195  }
9196  Utilities->CallLogPop(647);
9197 }
9198 
9199 // ---------------------------------------------------------------------------
9200 
9201 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
9202 {
9203  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
9204  for(int x = 0; x < 4; x++)
9205  {
9206  if(PlotElement[x] > -1)
9207  {
9208  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
9209  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
9210  }
9211  }
9212  Utilities->CallLogPop(1708);
9213 }
9214 
9215 // ---------------------------------------------------------------------------
9216 
9217 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
9218 {
9219  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
9220  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
9221  AnsiString(LinkNumber) + "," + HeadCode);
9222 
9223 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
9224  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
9225  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
9226 */
9227 
9228  // note that MidElement always fully occupied
9229  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
9230  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
9231  {
9232  Utilities->CallLogPop(2005);
9233  return(true);
9234  }
9235  if(Straddle == LeadMid)
9236  {
9237  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
9238  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
9239  {
9240  Utilities->CallLogPop(2006);
9241  return(true);
9242  }
9243  }
9244  else if(Straddle == LeadMidLag)
9245  {
9246  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
9247  // only interested in LeadEntryPos as train not occupying ExitPos yet
9248  {
9249  Utilities->CallLogPop(2007);
9250  return(true);
9251  }
9252  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
9253  // only interested in LagExitPos as train has left EntryPos
9254  {
9255  Utilities->CallLogPop(2008);
9256  return(true);
9257  }
9258  }
9259  Utilities->CallLogPop(2009);
9260  return(false);
9261 }
9262 
9263 // ---------------------------------------------------------------------------
9264 
9265 float TTrain::CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair) // only called for running trains.
9270 // TimeToExit & ExitPair added for multiplayer
9271 {
9272  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
9273  int DistanceToRedSignal = 0, DistanceToExit = -1;
9274  float TimeToAct = 0, LastTimeToExit = TimeToExit;
9275  TimeToExit = -1;
9276  ExitPair.first = -1;
9277  ExitPair.second = -1;
9278  float MinsEarly = 0; //added at v2.6.1
9279  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
9280  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
9281  float TempTTE;
9282 
9283  if(TrainFailed)
9284  {
9285  Utilities->CallLogPop(2147);
9286  return(0); // time to act now, time to exit set above
9287  }
9288  if(SignallerStopped)
9289  {
9290  Utilities->CallLogPop(2080);
9291  return(-1); //time to exit set above
9292  }
9293  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
9294  {
9295  Utilities->CallLogPop(2266);
9296  return(-1); // time to exit set above
9297  }
9298 
9299  // check if exiting at a continuation, if so there's no action time needed but still need exit time & ExitPair
9300  if((LeadElement == -1) && (MidElement == -1) && (LagElement > -1) && (Track->TrackElementAt(1411, LagElement).TrackType == Continuation)
9301  /*&& (ExitSpeedFull > 1)*/) //LagElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9302  {
9303  if(Straddle == LeadMidLag) //only half of rear train element on exit, 0.5 lengths to exit
9304  {
9305  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9306  {
9307  TempTTE = (0.5 * Track->TrackElementAt(1412, LagElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9308  if(TempTTE < LastTimeToExit)
9309  {
9310  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9311  }
9312  else
9313  {
9314  TimeToExit = LastTimeToExit;
9315  }
9316  }
9317  else
9318  {
9319  TimeToExit = LastTimeToExit;
9320  }
9321  ExitPair.first = Track->TrackElementAt(1413, LagElement).HLoc;
9322  ExitPair.second = Track->TrackElementAt(1414, LagElement).VLoc;
9323  Utilities->CallLogPop(2342);
9324  return(-1);
9325  }
9326  else
9327  {
9328  TimeToExit = 0; //all train exited
9329  ExitPair.first = Track->TrackElementAt(1415, LagElement).HLoc;
9330  ExitPair.second = Track->TrackElementAt(1416, LagElement).VLoc;
9331  Utilities->CallLogPop(2343);
9332  return(-1);
9333  }
9334  }
9335  if((LeadElement == -1) && (MidElement > -1) && (Track->TrackElementAt(1417, MidElement).TrackType == Continuation)/* && (ExitSpeedFull > 1)*/)
9336  //here MidElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9337  {
9338  if(Straddle == LeadMidLag) //front element of train half off the exit, 1.5 lengths to exit
9339  {
9340  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9341  {
9342  TempTTE = (1.5 * Track->TrackElementAt(1418, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9343  if(TempTTE < LastTimeToExit)
9344  {
9345  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9346  }
9347  else
9348  {
9349  TimeToExit = LastTimeToExit;
9350  }
9351  }
9352  else
9353  {
9354  TimeToExit = LastTimeToExit;
9355  }
9356  ExitPair.first = Track->TrackElementAt(1419, MidElement).HLoc;
9357  ExitPair.second = Track->TrackElementAt(1420, MidElement).VLoc;
9358  Utilities->CallLogPop(2344);
9359  return(-1);
9360  }
9361  else //front element of train fully off the exit, one length to exit
9362  {
9363  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9364  {
9365  TempTTE = (Track->TrackElementAt(1421, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9366  if(TempTTE < LastTimeToExit)
9367  {
9368  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9369  }
9370  else
9371  {
9372  TimeToExit = LastTimeToExit;
9373  }
9374  }
9375  else
9376  {
9377  TimeToExit = LastTimeToExit;
9378  }
9379  ExitPair.first = Track->TrackElementAt(1422, MidElement).HLoc;
9380  ExitPair.second = Track->TrackElementAt(1423, MidElement).VLoc;
9381  Utilities->CallLogPop(2345);
9382  return(-1);
9383  }
9384  }
9385  if(LeadElement > -1)
9386  {
9388  /* && (ExitSpeedFull > 1)*/) //LeadElement is the exit. If LeadMidLag have lead half on exit or if LeadMid have lead full on exit
9389  { //if LeadMidLag have 2.5 lengths to exit, else have 2 lengths to exit
9390  //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9391  if(Straddle == LeadMidLag)
9392  {
9393  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9394  {
9395  TempTTE = (2.5 * Track->TrackElementAt(1426, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9396  if(TempTTE < LastTimeToExit)
9397  {
9398  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9399  }
9400  else
9401  {
9402  TimeToExit = LastTimeToExit;
9403  }
9404  }
9405  else
9406  {
9407  TimeToExit = LastTimeToExit;
9408  }
9409  ExitPair.first = Track->TrackElementAt(1427, LeadElement).HLoc;
9410  ExitPair.second = Track->TrackElementAt(1428, LeadElement).VLoc;
9411  Utilities->CallLogPop(2346);
9412  return(-1);
9413  }
9414  else
9415  {
9416  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9417  {
9418  TempTTE = (2 * Track->TrackElementAt(1429, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9419  if(TempTTE < LastTimeToExit)
9420  {
9421  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9422  }
9423  else
9424  {
9425  TimeToExit = LastTimeToExit;
9426  }
9427  }
9428  else
9429  {
9430  TimeToExit = LastTimeToExit;
9431  }
9432  ExitPair.first = Track->TrackElementAt(1430, LeadElement).HLoc;
9433  ExitPair.second = Track->TrackElementAt(1431, LeadElement).VLoc;
9434  Utilities->CallLogPop(2347);
9435  return(-1);
9436  }
9437  }
9438  }
9439 //here if LeadElement > -1 and its forward connection also > -1
9440 
9441  // calc distance to next red signal
9442  if(!Stopped() || RevisedStoppedAtLoc())
9443  {
9444  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
9445  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
9446  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
9447 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
9448  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
9449  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
9450  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
9451  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
9452  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
9453  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
9454  before the train has stopped the current station is still recognised as a future stop.
9455  In signaller mode stops don't count, and if pass stop signal command is given then when have LeadMidLag the current element
9456  becomes the signal, and the time to act indication becomes 'NOW'.
9457 */
9458  {
9459  FirstPosToBeMeasured = LeadElement;
9460  FirstEntryPos = LeadEntryPos;
9461  }
9462  float CurrentStopTime; // set to 0 at start of function
9463  float LaterStopTime; // set to 0 at start of function
9464  float RecoverableTime; // set to 0 at start of function
9465  int AvTrackSpeed; // set to zero at start of function
9466  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
9467  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
9468  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
9469 //at this point can't have both DistanceToRedSignal and DistanceToExit both set. Either both will be unset (-1) or one will be set.
9470 //Therefore since need to calculate the time for each in the same way use a generic
9471 
9472  if((DistanceToRedSignal == -1) && (DistanceToExit == -1))// both unset so no action needed
9473  {
9474  TimeToExit = -1;
9475  Utilities->CallLogPop(2076);
9476  return(-1);
9477  }
9478 //else one or other is set
9479  bool DistanceToRedSignalSet = (DistanceToRedSignal > -1);
9480  bool DistanceToExitSet = (DistanceToExit > -1);
9481  int GenericDistance = DistanceToRedSignal;
9482  if(DistanceToExitSet)
9483  {
9484  GenericDistance = DistanceToExit;
9485  }
9486 /* Have MinsDelayed; pos or neg,
9487  CurrentStopTime; pos or zero
9488  LaterStopTime; pos or zero
9489  RecoverableTime; pos or zero
9490 
9491  & from these calculate TotalStopTime. noting that:
9492  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
9493  RecoverableTime always < LaterStopTime or both zero
9494  can't subtract more than RecoverableTime (MinsDelayed > 0)
9495  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
9496  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
9497  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
9498  if running early & stopped at location CurrentStopTime will automatically include the excess
9499 */
9500  float TimeToSubtract, TotalStopTime;
9501  if(MinsDelayed > RecoverableTime)
9502  {
9503  TimeToSubtract = RecoverableTime;
9504  }
9505  else
9506  {
9507  TimeToSubtract = MinsDelayed; // may be negative;
9508  }
9509  if((AvTrackSpeed > 0) && (DistanceToStationStop <= GenericDistance) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
9510  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
9511  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
9512  //next station stop
9513  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
9514  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
9515  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
9516  //first find departure time from the next stop
9517  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
9518  {
9519  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
9520  {
9523  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
9524  }
9525  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
9526  {
9528  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
9529  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
9530  {
9531  // must be a departure
9532  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
9533  }
9534  }
9535  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
9536  {
9537  MinsEarly = 0;
9538  }
9539  if(MinsEarly < 0)
9540  {
9541  MinsEarly = 0;
9542  }
9543  }
9544  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
9545  {
9546  if(CurrentStopTime > 0)
9547  {
9548  TotalStopTime = CurrentStopTime + LaterStopTime;
9549  }
9550  // stopped at loc, will depart on time
9551  else
9552  {
9553  TotalStopTime = LaterStopTime - MinsDelayed;
9554  }
9555  // not stopped, will depart on time at first later stop so add the delay
9556  }
9557  else if((MinsEarly > 0) && !Stopped()) //running early
9558  {
9559  TotalStopTime = LaterStopTime + MinsEarly;
9560  }
9561  else // on time or running late
9562  {
9563  if(LaterStopTime == 0)
9564  {
9565  TotalStopTime = CurrentStopTime;
9566  }
9567  // no later stops, if stopped now will depart as soon as possible,
9568  // if not stopped no stop times to add
9569  else
9570  {
9571  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
9572  }
9573  }
9574  if(AvTrackSpeed < 30)
9575  {
9576  AvTrackSpeed = 30;
9577  }
9578  int Speed = AvTrackSpeed;
9579  if(AvTrackSpeed > int(MaxRunningSpeed))
9580  {
9581  Speed = int(MaxRunningSpeed);
9582  }
9583  if(TrainMode == Signaller)
9584  {
9585  Speed = SignallerMaxSpeed;
9586  TotalStopTime = 0;
9587  }
9588  if(DistanceToRedSignalSet)
9589  {
9590  TimeToAct = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9591  // accel & decel taken into account in
9592  // CalcDistanceToRedSignalandStopTime
9593  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9594  TimeToExit = -1;
9595  Utilities->CallLogPop(2079);
9596  return(TimeToAct);
9597  }
9598  else //DistanceToExitSet must be true
9599  {
9600  TimeToExit = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9601  // accel & decel taken into account in
9602  // CalcDistanceToRedSignalandStopTime
9603  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9604  Utilities->CallLogPop(2370);
9605  return(-1); //no red signal so no time to act
9606  }
9607  }
9608  else // stopped not at location
9609  {
9611  {
9612  TimeToAct = 0.0;
9613  TimeToExit = -1;
9614  }
9615  if(StoppedWithoutPower) //added at v2.13.2 as this situation was missed & time to act was 0 [If train failed then covered above]
9616  {
9617  TimeToAct = -1;
9618  TimeToExit = -1;
9619  }
9620  // but if stopped at a signal & autosigs route after it then ok, provided signal not failed
9621  if(StoppedAtSignal)
9622  {
9623  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
9624  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
9625  bool NextElementFailed = Track->TrackElementAt(1548, NextElement).Failed; //added at v2.13.2
9626  int NextExitPos;
9627  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
9628  {
9629  if((NextEntryPos == 0) || (NextEntryPos == 2))
9630  // leading entry point
9631  {
9632  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
9633  {
9634  NextExitPos = 1;
9635  }
9636  else
9637  {
9638  NextExitPos = 3;
9639  }
9640  }
9641  else
9642  {
9643  NextExitPos = 0; // trailing entry point
9644  }
9645  }
9646  else
9647  {
9648  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
9649  }
9650  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
9651  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
9652  int RouteNumber; // holder for referenced value, not used
9653  if((AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !NextElementFailed)
9654  { //NextElementFailed added at v2.13.2
9655  TimeToAct = -1;
9656  TimeToExit = -1;
9657  }
9658  }
9659  Utilities->CallLogPop(2074);
9660  return(TimeToAct);
9661  }
9662 }
9663 
9664 // ---------------------------------------------------------------------------
9665 
9667 {
9668  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
9669  if(LeadElement > -1)
9670  {
9672  {
9673  Utilities->CallLogPop(2148);
9674  return(true);
9675  }
9676  }
9677  if(MidElement > -1)
9678  {
9680  {
9681  Utilities->CallLogPop(2149);
9682  return(true);
9683  }
9684  }
9685  if(LagElement > -1)
9686  {
9688  {
9689  Utilities->CallLogPop(2150);
9690  return(true);
9691  }
9692  }
9693  Utilities->CallLogPop(2151);
9694  return(false);
9695 }
9696 
9697 // ---------------------------------------------------------------------------
9698 // TTrainController
9699 // ---------------------------------------------------------------------------
9700 
9702 {
9703  OnTimeArrivals = 0;
9704  LateArrivals = 0;
9705  EarlyArrivals = 0;
9706  OnTimePasses = 0;
9707  LatePasses = 0;
9708  EarlyPasses = 0;
9709  OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
9710  LateExits = 0;
9711  EarlyExits = 0;
9712  OnTimeDeps = 0;
9713  LateDeps = 0;
9714  MissedStops = 0;
9715  OtherMissedEvents = 0;
9716  UnexpectedExits = 0;
9717  NumFailures = 0;
9718  IncorrectExits = 0;
9719  SPADEvents = 0;
9720  SPADRisks = 0;
9721  CrashedTrains = 0;
9722  Derailments = 0;
9723  TotArrDepPass = 0;
9724  TotLateArrMins = 0;
9725  TotEarlyArrMins = 0;
9726  TotLatePassMins = 0;
9727  TotEarlyPassMins = 0;
9728  TotLateExitMins = 0; //added at v2.9.1
9729  TotEarlyExitMins = 0; //added at v2.9.1
9730  TotLateDepMins = 0;
9731  ExcessLCDownMins = 0;
9732  SkippedTTEvents = 0; //added at v2.11.0
9733  TTClockTime = 0; // added for v0.6
9735  // added at v1.3.0 to ensure false at start
9736  OpTimeToActUpdateCounter = 0; // new v2.2.0
9737  OpActionPanelVisible = false; // new v2.2.0
9738  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
9739  SSHigh = false;
9740  MRSHigh = false;
9741  MRSLow = false;
9742  MassHigh = false;
9743  BFHigh = false;
9744  BFLow = false;
9745  PwrHigh = false;
9746  SigSHigh = false;
9747  SigSLow = false;
9748  randomize();
9749  // to seed rand() & random() with a random number (see UpdateTrain)
9750 }
9751 
9752 // ---------------------------------------------------------------------------
9753 
9755 {
9756  for(unsigned int x = 0; x < TrainVector.size(); x++)
9757  {
9758  TrainVectorAt(32, x).DeleteTrain(4);
9759  }
9760  TrainVector.clear();
9761 }
9762 
9763 // ---------------------------------------------------------------------------
9764 
9765 void TTrainController::LogEvent(AnsiString Str)
9766 {
9767  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
9768 
9769  // restrict to last 1000 entries
9770  Utilities->EventLog.push_back(FullStr);
9771  if(Utilities->EventLog.size() > 1000)
9772  {
9773  Utilities->EventLog.pop_front();
9774  }
9775 }
9776 
9777 // ---------------------------------------------------------------------------
9778 
9780 {
9781  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
9782  bool ClockState = Utilities->Clock2Stopped;
9783  Utilities->Clock2Stopped = true;
9784  // new section dealing with Snt & Snt-sh additions
9785  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
9786  // clock tick after stops flashing
9788  {
9789  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9790  {
9791  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
9792  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
9793  TActionEventType EventType = NoEvent;
9794  if(AVEntry0.Command == "Snt")
9795  {
9796  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9797  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9798  int IncrementalMinutes = 0;
9799  int IncrementalDigits = 0;
9800  if(AVEntryLast.FormatType == Repeat)
9801  {
9802  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9803  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9804  }
9805  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9806  {
9807  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
9808  }
9809  // see above note
9810 
9811  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9812  {
9813  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
9814  if(TTOD.RunningEntry != NotStarted)
9815  {
9816  continue;
9817  }
9818 
9819 //Multiplayer: here check for a train entering at a coupling {RearStartOrRepeatMins shows if it's a coupling or not), and can only be a Snt entry
9820 //if so and no arrival signalled yet bypass the timetabled arrival
9821 //if so and arrival signalled then start the new service, using the repeat number and headcode for the entering train
9822 //if a repeat is skipped then should be ok if it arrives later as its RunningEntry is still NotStarted
9823 
9824  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
9825  {
9826  break; // all the rest will also be greater
9827  }
9828  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
9829  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9830  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
9831  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
9832  {
9833  TTOD.TrainID = TrainVector.back().TrainID;
9834  TTOD.RunningEntry = Running;
9835  }
9836  else if(EventType == FailTrainEntry)
9837  {
9838  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9839  }
9840  }
9841  }
9842  if(AVEntry0.Command == "Snt-sh")
9843  // just start this once, shuttle repeats take care of restarts
9844  {
9845  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9846  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9847  int IncrementalMinutes = 0;
9848  int IncrementalDigits = 0;
9849  if(AVEntryLast.FormatType == Repeat)
9850  {
9851  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9852  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9853  }
9854  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9855  {
9856  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
9857  }
9858  // see above note
9859  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
9860  if(TTOD.RunningEntry == NotStarted)
9861  {
9862  if(AVEntry0.EventTime <= TTClockTime)
9863  {
9864  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9865  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
9866  TDEntry.SignallerSpeed, false, EventType))
9867  // false for SignallerControl
9868  {
9869  TTOD.TrainID = TrainVector.back().TrainID;
9870  TTOD.RunningEntry = Running;
9871  }
9872  else if(EventType == FailTrainEntry)
9873  {
9874  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9875  }
9876  }
9877  }
9878  }
9879  }
9880  }
9881 
9882  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
9883  // iteration, next cycle will catch up with any other pending updates
9884  if(!TrainVector.empty())
9885  {
9886  TrainAdded = false;
9887 
9888 //elapsed time investigations
9889 
9890 //elapsed time segment
9891 //double Start, End;
9892 //AnsiString ElapsedTimeReport = "";
9893 //end elasped time segment
9894  AllRoutes->CallonVector.clear(); // this will be rebuilt during the calls to UpdateTrain
9895 //elapsed time segment
9896 //PerfLogForm->PerformanceLog(-1, "\n Train vector size: " + AnsiString(TrainVector.size()) + '\n');
9897 //PerfLogForm->PerformanceLog(-1, "Start time list");
9898 //end elapsed time segment
9899  for(unsigned int x = 0; x < TrainVector.size(); x++)
9900  {
9901 //elapsed time segment
9902 //Start = double(GetTime()) * 86400; //secs
9903 //end elapsed time segment
9904  TrainVectorAt(33, x).UpdateTrain(0);
9905 //elapsed time segment
9906 //End = double(GetTime()) * 86400;
9907 //ElapsedTimeReport = TrainVectorAt(-1, x).TrainDataEntryPtr->ServiceReference + AnsiString(" ") + AnsiString(int((End - Start) * 1000)); //msecs
9908 //PerfLogForm->PerformanceLog(-1, ElapsedTimeReport);
9909 //end elapsed time segment
9910 
9911 //end elapsed time investigations
9912 
9913  /* added HasTrainGone() condition below in v0.4c to prevent 2 trains both having TrainGone set in UpdateTrain
9914  at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
9915  iterates in reverse to erase the second train to have gone (when the first train to have gone comes before the second in TrainVector),
9916  but afterwards ReplotTrains iterates forwards and therefore replots the first train to have gone and therefore sets the TrainIDOnElement value
9917  to the exited train, with nothing to reset it. Hovering the mouse over that element with train information enabled causes an error because
9918  the track element thinks the train is still there, whereas it is missing from the TrainVector. BUT subsequently (in v2.11.1) changed RePlotTrains
9919  so it doesn't plot trains with TrainGone set, but left this is as does no harm
9920 
9921  Had another error notified by Kevin Smith on 02/01/22 where a train was manually removed in the same clock cycle as a train exited, and this caused
9922  the same error as above. Did a lot of experimenting but eventually cured it with two changes, first as above in RePlotTrains, and also below adding
9923  a break; command after one TrainHasGone() dealt with. There were introduced in v2.11.1 & seems ok now
9924 
9925  These changes should deal with any number of TrainGone flags set in the same clock cycle - from exiting, manual removal, or joins
9926  */
9927  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
9928  {
9929  break; //only one exited train will be dealt with at a time (see below) so no point looking further
9930  }
9931  }
9932  // set warning flags
9933  CrashWarning = false;
9934  DerailWarning = false;
9935  SPADWarning = false;
9936  CallOnWarning = false;
9937  SignalStopWarning = false;
9938  BufferAttentionWarning = false;
9939  TrainFailedWarning = false;
9940  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
9941  {
9942  TTrain &Train = TrainVectorAt(34, x);
9943  if(Train.Crashed)
9944  // can't use background colours for crashed & derailed because same colour
9945  {
9946  CrashWarning = true;
9947  }
9948  else if(Train.Derailed)
9949  // can't use background colours for crashed & derailed because same colour
9950  {
9951  DerailWarning = true;
9952  }
9953  else if(Train.BackgroundColour == clSPADBackground)
9954  // use colour as that changes as soon as passes signal
9955  {
9956  SPADWarning = true;
9957  }
9958  else if(Train.BackgroundColour == clTrainFailedBackground)
9959  {
9960  TrainFailedWarning = true;
9961  }
9962  else if(Train.BackgroundColour == clCallOnBackground)
9963  // use colour as also stopped at signal
9964  {
9965  CallOnWarning = true;
9966  }
9967  else if(Train.BackgroundColour == clSignalStopBackground)
9968  // use colour to distinguish from call-on
9969  {
9970  SignalStopWarning = true;
9971  }
9972  else if(Train.BackgroundColour == clBufferAttentionNeeded)
9973  // use colour to distinguish from ordinary buffer stop
9974  {
9975  BufferAttentionWarning = true;
9976  }
9977  if(Train.HasTrainGone())
9978  {
9979  AnsiString Loc = "";
9980  bool ElementFound = false;
9981  TTrackElement TE;
9982  if(Train.LagElement > -1)
9983  {
9984  TE = Track->TrackElementAt(531, Train.LagElement);
9985  ElementFound = true;
9986  }
9987  else if(Train.MidElement > -1)
9988  {
9989  TE = Track->TrackElementAt(779, Train.MidElement);
9990  ElementFound = true;
9991  }
9992  else if(Train.LeadElement > -1)
9993  {
9994  TE = Track->TrackElementAt(780, Train.LeadElement);
9995  ElementFound = true;
9996  }
9997  if(ElementFound)
9998  {
9999  if(TE.ActiveTrackElementName != "")
10000  {
10001  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
10002  }
10003  else
10004  {
10005  Loc = "track element " + TE.ElementID;
10006  }
10007  }
10008  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
10009  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
10010  // need above first because may also have ActionVectorEntryPtr == "Fer"
10011  {
10012  Train.UnplotTrain(9);
10013  // added at v1.3.0 to reset signals after train removed from an autosigsroute
10015  {
10018  }
10019  // end of addition
10020  AllRoutes->RebuildRailwayFlag = true;
10021  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
10022  // correctly after a crash
10023  }
10024  else if(AVEntryPtr->Command == "Fer")
10025  {
10026  bool CorrectExit = false;
10027  if(!AVEntryPtr->ExitList.empty())
10028  {
10029  for(TNumListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
10030  {
10031  if(*ELIT == Train.LagElement)
10032  {
10033  CorrectExit = true;
10034  }
10035  }
10036  }
10037  if(CorrectExit)
10038  {
10039  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
10040  }
10041  else
10042  {
10043  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
10044  }
10045  }
10046  else
10047  {
10048  if(!AVEntryPtr->SignallerControl)
10049  {
10050  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
10051  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
10052  // -2 is marker for send messages for all remaining actions except Fer if present
10053  }
10054  else
10055  {
10056  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
10057  }
10058  }
10059  Utilities->CumulativeDelayedRandMinsAllTrains += Train.CumulativeDelayedRandMinsOneTrain; //added at v2.13.0 for random delays
10060  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
10061  Train.DeleteTrain(1);
10062  TrainVector.erase(TrainVector.begin() + x);
10063  ReplotTrains(1, Display); //to reset ElementIDs for remaining trains when have removed a train
10064  //NB: won't plot any trains with TrainGone flag set (changed at v2.11.1)
10065  break; //added at v2.11.1 to ensure that only one train with TrainGone set is dealt with in one clock cycle
10066  }
10067  }
10068  }
10069  else
10070  {
10071  // reset all flags in case last train removed with flag set
10072  CrashWarning = false;
10073  DerailWarning = false;
10074  SPADWarning = false;
10075  CallOnWarning = false;
10076  SignalStopWarning = false;
10077  BufferAttentionWarning = false;
10078  TrainFailedWarning = false;
10079  }
10080  // update OpTimeToActMultimap
10082  {
10084  // clears entries then adds values for running trains then for continuation entries
10086  //added for multiplayer for running trains only
10087  }
10088  Utilities->Clock2Stopped = ClockState;
10089  Utilities->CallLogPop(723);
10090 }
10091 
10092 // ---------------------------------------------------------------------------
10094 {
10095  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
10096  if(!TrainVector.empty())
10097  {
10098  for(int x = TrainVector.size() - 1; x >= 0; x--)
10099  {
10100  TrainVectorAt(50, x).DeleteTrain(2);
10101  }
10102  TrainVector.clear();
10103  }
10104  if(!TrainDataVector.empty())
10105  {
10106  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
10107  {
10108  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
10109  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
10110  {
10111  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
10112  TOD.RunningEntry = NotStarted;
10113  TOD.TrainID = -1;
10114  TOD.EventReported = NoEvent;
10115  }
10116  }
10117  }
10118  Display->GetOutputLog1()->Caption = "";
10119  Display->GetOutputLog2()->Caption = "";
10120  Display->GetOutputLog3()->Caption = "";
10121  Display->GetOutputLog4()->Caption = "";
10122  Display->GetOutputLog5()->Caption = "";
10123  Display->GetOutputLog6()->Caption = "";
10124  Display->GetOutputLog7()->Caption = "";
10125  Display->GetOutputLog8()->Caption = "";
10126  Display->GetOutputLog9()->Caption = "";
10127  Display->GetOutputLog10()->Caption = "";
10128  Utilities->CallLogPop(1352);
10129 }
10130 
10131 // ---------------------------------------------------------------------------
10132 
10134 {
10135  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
10136  if(!TrainVector.empty())
10137  {
10138  for(unsigned int x = 0; x < TrainVector.size(); x++)
10139  {
10140  if(!TrainVectorAt(84, x).HasTrainGone()) //added at v2.11.0 to prevent plotting a train pending removal & particularly to prevent TrainElementID's being reinstated
10141  { //see Kevin Smith error information for details
10142  TrainVectorAt(51, x).PlotTrain(4, Disp);
10143  }
10144  }
10145  }
10146  Utilities->CallLogPop(724);
10147 }
10148 
10149 // ---------------------------------------------------------------------------
10150 
10151 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
10152 {
10153  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
10154  if(!TrainVector.empty())
10155  {
10156  for(unsigned int x = 0; x < TrainVector.size(); x++)
10157  {
10158  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
10159  }
10160  }
10161  Utilities->CallLogPop(1707);
10162 }
10163 
10164 // ---------------------------------------------------------------------------
10165 
10167 {
10168  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
10169  if(!TrainVector.empty())
10170  {
10171  for(unsigned int x = 0; x < TrainVector.size(); x++)
10172  {
10173  TrainVectorAt(52, x).UnplotTrain(10);
10174  }
10175  }
10177  Utilities->CallLogPop(725);
10178 }
10179 
10180 // ---------------------------------------------------------------------------
10181 
10182 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
10183  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
10184  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
10185 {
10186  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
10187  "," + AnsiString(Mass) + "," + ModeStr);
10188  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
10189  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr); //at v2.11.1 dropped later headcode - was listed twice
10190 
10191  int RearExitPos = -1;
10192 
10193  for(int x = 0; x < 4; x++)
10194  {
10195  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
10196  {
10197  RearExitPos = x;
10198  }
10199  }
10200  if(RearExitPos == -1)
10201  {
10202  throw Exception("Error, RearExit == -1 in AddTrain");
10203  }
10204  bool ReportFlag = true;
10205 
10206  // used to stop repeated messages from CheckStartAllowable when split failed
10207  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
10208  {
10209  ReportFlag = false;
10210  }
10211  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
10212  {
10213  // messages sent to performance log in CheckStartAllowable if ReportFlag true
10214  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
10215  Utilities->CallLogPop(938);
10216  return(false);
10217  }
10218  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
10219  TTrainMode TrainMode = NoMode;
10220 
10221  if(ModeStr == "Timetable")
10222  {
10223  TrainMode = Timetable;
10224  }
10225  // all else gives 'None', 'Signaller' set within program
10226 
10227  if(MaxRunningSpeed < 10)
10228  {
10229  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
10230  }
10231  if(SignallerSpeed < 10)
10232  {
10233  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
10234  }
10235  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
10236  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
10237 
10238  LogEvent("AddTrainSupplemental: Service Ref = " + TrainDataEntryPtr->ServiceReference + ", TrainID = " + AnsiString(NewTrain->TrainID)); //new at v2.11.1 so can relate headcode to ID
10239 
10240  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
10241  // initialise here rather than in TTrain constructor as create trains
10242  // with Null TrainDataEntryPtr when loading session trains
10243  if(SignallerControl)
10244  {
10245  NewTrain->TimetableFinished = true;
10246  NewTrain->SignallerStoppingFlag = false;
10247  NewTrain->TrainMode = Signaller;
10248  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
10249  {
10250  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
10251  }
10253  }
10254  // deal with starting conditions:-
10255  // unlocated Snt: just report entry & advance pointer
10256  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
10257  // Sns doesn't need a new train
10258  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
10259  // covers all above located starts
10260  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
10261  // wouldn't have accepted the timetable
10262  {
10263  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
10264  // StoppedAtBuffers is set in UpdateTrain()
10265  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
10266  // buffer end must be ahead of train or would have failed start position check
10267  {
10268  NewTrain->StoppedAtLocation = true;
10269  NewTrain->PlotStartPosition(0);
10271  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
10272  NewTrain->ActionVectorEntryPtr->Warning);
10273  if(!SignallerControl) // don't advance if SignalControlEntry
10274  {
10275  NewTrain->ActionVectorEntryPtr++;
10276  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
10277  }
10278  NewTrain->LastActionTime = TTClockTime;
10279  }
10280  // else a through station stop
10281  else
10282  {
10283  NewTrain->StoppedAtLocation = true;
10284  NewTrain->PlotStartPosition(10);
10286  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
10287  NewTrain->ActionVectorEntryPtr->Warning);
10288  if(!SignallerControl) // don't advance if SignalControlEntry
10289  {
10290  NewTrain->ActionVectorEntryPtr++;
10291  }
10292  NewTrain->LastActionTime = TTClockTime;
10293  }
10294  }
10295  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
10296  {
10297  NewTrain->PlotStartPosition(11);
10298  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
10299  AnsiString Loc = "";
10300  if(TE.ActiveTrackElementName != "")
10301  {
10302  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
10303  }
10304  else
10305  {
10306  Loc = "track element " + TE.ElementID;
10307  }
10308  if(TE.TrackType == Continuation)
10309  {
10310  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
10311  }
10312  else
10313  {
10314  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
10315  }
10316  if(!SignallerControl) // don't advance if SignalControlEntry
10317  {
10318  NewTrain->ActionVectorEntryPtr++;
10319  }
10320  NewTrain->LastActionTime = TTClockTime;
10321  // no need to set LastActionTime for an unlocated entry
10322  }
10323  // cancel a wrong-direction route if either element of train starts on one
10324  if(NewTrain->LeadElement > -1)
10325  {
10326  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
10327  }
10328  if(NewTrain->MidElement > -1)
10329  {
10330  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
10331  }
10332  // set signals for a right-direction autosigs route for either element of train on one
10333  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
10334  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
10335  int RouteNumber = -1;
10336  bool SignalsSet = false;
10337 
10338  if(NewTrain->LeadElement > -1)
10339  {
10340  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
10341  {
10342  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
10343  int RouteStartPosition;
10344  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
10345  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
10346  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
10347  if(FirstPair.first == RouteNumber)
10348  {
10349  RouteStartPosition = FirstPair.second;
10350  }
10351  else if(SecondPair.first == RouteNumber)
10352  {
10353  RouteStartPosition = SecondPair.second;
10354  }
10355  else
10356  {
10357  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
10358  }
10359  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
10360  SignalsSet = true;
10361  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
10362  }
10363  else if(RouteNumber > -1) // non-autosigsroute
10364  {
10365  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
10366  int FirstTVPos = TempPDE.GetTrackVectorPosition();
10367  int FirstELinkPos = TempPDE.GetELinkPos();
10368  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
10369  {
10370  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10371  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
10372  }
10373  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
10374  {
10375  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10376  // remove the last element under LeadElement
10377  }
10378  AllRoutes->RebuildRailwayFlag = true;
10379  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
10380  // now deal with a rear linked autosigs route
10381  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
10382  {
10383  int LinkedRouteNumber = -1;
10384  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
10385  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
10386  {
10387  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
10388  // this is ok as here we are setting signals from the start of the route
10389  }
10390  }
10391  SignalsSet = true;
10392  }
10393  }
10394  if(NewTrain->MidElement > -1)
10395  // if entering at a continuation MidElement == -1
10396  {
10397  // this is included in case a train starts with LeadElement on no route and MidElement on a route
10398  if(!SignalsSet)
10399  {
10400  RouteNumber = -1;
10401  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
10402  {
10403  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
10404  int RouteStartPosition;
10405  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
10406  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
10407  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
10408  if(FirstPair.first == RouteNumber)
10409  {
10410  RouteStartPosition = FirstPair.second;
10411  }
10412  else if(SecondPair.first == RouteNumber)
10413  {
10414  RouteStartPosition = SecondPair.second;
10415  }
10416  else
10417  {
10418  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
10419  }
10420  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
10421  SignalsSet = true;
10422  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
10423  }
10424  else if(RouteNumber > -1) // non-autosigsroute
10425  {
10426  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
10427  int FirstTVPos = TempPDE.GetTrackVectorPosition();
10428  int FirstELinkPos = TempPDE.GetELinkPos();
10429  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
10430  {
10431  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10432  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
10433  }
10434  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
10435  {
10436  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10437  // remove the last element under LeadElement
10438  }
10439  AllRoutes->RebuildRailwayFlag = true;
10440  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
10441  // now deal with a rear linked autosigs route
10442  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
10443  {
10444  int LinkedRouteNumber = -1;
10445  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
10446  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
10447  {
10448  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
10449  // this is ok as now we are setting signals from the start of the route
10450  }
10451  }
10452  }
10453  }
10454  }
10455  TrainVector.push_back(*NewTrain);
10456  Utilities->CallLogPop(731);
10457  return(true);
10458 }
10459 
10460 // ---------------------------------------------------------------------------
10461 
10462 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
10463 {
10464  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
10465  AnsiString(TrackVectorNumber));
10466  int VecPos = -1;
10467 
10468  for(unsigned int x = 0; x < TrainVector.size(); x++)
10469  {
10470  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
10471  {
10472  VecPos = x;
10473  }
10474  }
10475  if(VecPos == -1)
10476  {
10477  throw Exception("Error, VecPos not set in EntryPos");
10478  }
10479  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
10480  {
10481  Utilities->CallLogPop(734);
10482  return(TrainVectorAt(3, VecPos).LeadEntryPos);
10483  }
10484  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
10485  {
10486  Utilities->CallLogPop(735);
10487  return(TrainVectorAt(5, VecPos).MidEntryPos);
10488  }
10489  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
10490  {
10491  Utilities->CallLogPop(736);
10492  return(TrainVectorAt(7, VecPos).LagEntryPos);
10493  }
10494  Utilities->CallLogPop(737);
10495  return(-1);
10496 }
10497 
10498 // ---------------------------------------------------------------------------
10499 
10501 {
10502  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
10503  for(unsigned int x = 0; x < TrainVector.size(); x++)
10504  {
10505  if(TrainVectorAt(53, x).TrainID == TrainID)
10506  {
10507  Utilities->CallLogPop(738);
10508  return(TrainVectorAt(54, x));
10509  }
10510  }
10511  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
10512 }
10513 
10514 // ---------------------------------------------------------------------------
10515 
10516 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
10517 // return true if find the train (added at v2.4.0 as can select a removed train in
10518 // ActionsDueListBox before it updates - reported by LiWinDom in error report 23/04/20)
10519 {
10520  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
10521  for(unsigned int x = 0; x < TrainVector.size(); x++)
10522  {
10523  if(TrainVectorAt(69, x).TrainID == TrainID)
10524  {
10525  Utilities->CallLogPop(2152);
10526  return(true);
10527  }
10528  }
10529  Utilities->CallLogPop(2153);
10530  return(false);
10531 }
10532 
10533 // ---------------------------------------------------------------------------
10534 
10535 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
10536 {
10537  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
10538  Utilities->Format96HHMMSS(Time));
10539  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
10540 
10541  Utilities->CallLogPop(2061);
10542  return(RepeatTime);
10543 }
10544 
10545 // ---------------------------------------------------------------------------
10546 
10547 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
10548 // Enter with Ptr pointing to first action to be listed (i.e. next action)
10549 {
10550  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
10551  AnsiString RetStr = "", PartStr = "";
10552  int Count = 0;
10553  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
10554 
10555  Ptr--; // because incremented at start of loop
10556  do
10557  {
10558  Ptr++;
10559  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
10560  {
10561  continue; // move past the starting entry
10562  }
10563  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
10564  {
10565  break;
10566  }
10567  if(Ptr->SignallerControl)
10568  {
10569  RetStr = "Train under signaller control";
10570  break;
10571  }
10572  if(Ptr->FormatType == TimeTimeLoc)
10573  {
10574  if(Ptr->ArrivalTime == Ptr->DepartureTime)
10575  {
10576  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
10577  }
10578  else
10579  {
10580  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
10581  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10582  Count++; // because there are 2 entries
10583  }
10584  }
10585  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
10586  {
10587  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
10588  }
10589  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
10590  {
10591  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10592  }
10593  else if(Ptr->FormatType == PassTime) // new
10594  {
10595  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
10596  }
10597  else if(Ptr->Command == "Fns")
10598  {
10599  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10600  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10601  PartStr = ControllerGetNewServiceDepartureInfo(11, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
10602  }
10603  else if(Ptr->Command == "F-nshs")
10604  {
10605  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10606  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
10607  PartStr = ControllerGetNewServiceDepartureInfo(13, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10608  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
10609  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
10610  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
10611  }
10612 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
10613  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10614  {
10615  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10616  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10617  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10618  PartStr = ControllerGetNewServiceDepartureInfo(15, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10619  }
10620  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10621  {
10622  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10623  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
10624  PartStr = ControllerGetNewServiceDepartureInfo(17, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10625  }
10626  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10627  {
10628  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10629  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10630  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10631  PartStr = ControllerGetNewServiceDepartureInfo(19, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
10632  }
10633  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10634  {
10635  PartStr = "Terminate at " + Ptr->LocationName;
10636  }
10637  else if(Ptr->Command == "Frh")
10638  {
10639  PartStr = "Terminate at " + Ptr->LocationName;
10640  }
10641  else if(Ptr->Command == "Fer")
10642  {
10643  AnsiString AllowedExits;
10644  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
10645  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
10646  }
10647  else if(Ptr->Command == "Fjo")
10648  {
10649  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
10650  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10651  }
10652  else if(Ptr->Command == "jbo")
10653  {
10654  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
10655  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10656  }
10657  else if(Ptr->Command == "fsp")
10658  {
10659  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
10660  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10661  }
10662  else if(Ptr->Command == "rsp")
10663  {
10664  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
10665  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10666  }
10667  else if(Ptr->Command == "cdt")
10668  {
10669  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
10670  }
10671  if(RetStr != "")
10672  {
10673  RetStr = RetStr + '\n' + PartStr;
10674  }
10675  else
10676  {
10677  RetStr = PartStr;
10678  }
10679  Count++;
10680  }
10681  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
10682  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
10683  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
10684  // forward as anyone should wish to see without looking at the full timetable
10685  Utilities->CallLogPop(2072);
10686  return(RetStr);
10687 }
10688 
10689 // ---------------------------------------------------------------------------
10690 
10691 AnsiString TTrainController::ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
10692 {
10693  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
10694  + AnsiString(RptNum) + ",ControllerGetNewServiceDepartureInfo," + TDEPtr->HeadCode);
10695  AnsiString DepTime = "", EventTime = "";
10696  bool CDTFlag = false; //reports if train changes direction before departs
10697  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
10698  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
10699  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
10700  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
10701  {
10702  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
10703  {
10704  TowardsLocation = AVI->LocationName;
10705  }
10706  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
10707  {
10708  TTrackElement TE = Track->TrackElementAt(1453, (AVI->ExitList.front()));
10709  if(TE.ActiveTrackElementName != "")
10710  {
10711  TowardsLocation = TE.ActiveTrackElementName;
10712  }
10713  else
10714  {
10715  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
10716  }
10717  }
10718  }
10719  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
10720  {
10721  if(AVI->Command == "cdt")
10722  {
10723  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
10724  continue;
10725  }
10726  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
10727  {
10728  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
10729  RetStr += "\nNew service splits at " + EventTime;
10730  Utilities->CallLogPop(2237);
10731  return(RetStr);
10732  }
10733  if(AVI->Command == "jbo")
10734  {
10735  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
10736  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
10737  Utilities->CallLogPop(2238);
10738  return(RetStr);
10739  }
10740  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
10741  {
10742  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
10743  if(CDTFlag)
10744  {
10745  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10746  {
10747  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
10748  }
10749  else
10750  {
10751  RetStr += "\nNew service changes direction then departs at " + DepTime;
10752  }
10753  }
10754  else
10755  {
10756  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10757  {
10758  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
10759  }
10760  else
10761  {
10762  RetStr += "\nNew service departs at " + DepTime;
10763  }
10764  }
10765  Utilities->CallLogPop(2239);
10766  return(RetStr);
10767  }
10768  }
10769  Utilities->CallLogPop(2223);
10770  return(RetStr);
10771 }
10772 
10773 // ---------------------------------------------------------------------------
10774 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
10775 /*
10776  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
10777  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
10778  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
10779 
10780  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
10781  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
10782  user wishes
10783 
10784  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
10785  descriptive text or anything user wishes
10786  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
10787  be ignored) is taken as the timetable start time.
10788  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
10789  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
10790  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
10791  within the timetable if required.
10792  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
10793  services)
10794  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
10795  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
10796 
10797  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
10798  text timetable file easier
10799 
10800  form:-
10801  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
10802  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
10803  then multiple entries, separated by commas, of the form:-
10804 
10805  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
10806  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
10807  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
10808 
10809  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
10810  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
10811  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
10812 
10813  HH:MM;Command (cdt) }TimeCmd }
10814  HH:MM;Location (arr & dep) }TimeLoc }
10815  HH:MM;HH:MM;Location }TimeTimeLoc }
10816  HH:MM;pas;Location }PassTime }
10817  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
10818  HH:MM;Fer;set of allowable IDs }ExitRailway }
10819  Command (Frh only) }FinRemHere }
10820 
10821  R;mm;dd;nn. Repeat Repeat entry
10822 
10823  Formats:
10824 
10825  Command only: Frh
10826  Time;Command: cdt
10827  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
10828  Time;Command;2 Element IDs: Snt
10829  Time;Comand;n Element IDs: Fer
10830  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
10831  Time;Command;2 Element IDs;Headcode Snt-sh
10832  Time;Command;Location pas
10833  Time;Location Arr Dep
10834  Time;Time;Location Arr & dep together
10835 
10836  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
10837 
10838  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
10839  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
10840  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
10841 
10842  4 2x Linked entries, all shuttles:
10843 
10844  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
10845  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
10846  -> Remain Here (at finish location after all repeats)
10847  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
10848  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
10849 
10850  Allowable successors:-
10851 
10852  Successor state Type
10853 
10854  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
10855  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
10856  Sfs AtLoc )
10857  Sns AtLoc ) Start
10858  Sns-fsh AtLoc )
10859  Snt-sh AtLoc )
10860  Sns-sh AtLoc )
10861 
10862  pas Moving )
10863  jbo AtLoc )
10864  fsp AtLoc )
10865  rsp AtLoc ) Intermediate
10866  cdt AtLoc )
10867  TimeLoc arr Moving (bef), AtLoc (aft) )
10868  TimeLoc dep AtLoc (bef), Moving (aft) )
10869  TimeTimeLoc Moving )
10870 
10871  Fns Repeat/Nothing)
10872  Fjo Repeat/Nothing)
10873  Frh Repeat/Nothing)
10874  Fer Repeat/Nothing) Finish
10875  Frh-sh Repeat )
10876  Fns-sh Repeat )
10877  F-nshs Nothing )
10878 
10879  Descriptions:
10880  Snt New train
10881  Sfs New service from split
10882  Sns New service from another service
10883  Sns-fsh New non-repeating service from a shuttle service
10884  Snt-sh New shuttle train at a timetabled stop
10885  Sns-sh New shuttle service from a feeder service
10886 
10887  pas Pass
10888  jbo Be joined by another train
10889  fsp Front split
10890  rsp Rear split
10891  cdt Change direction of train
10892  TimeLoc arr Arrival
10893  TimeLoc dep Departure
10894  TimeTimeLoc Arrival and departure
10895 
10896  Fns Finish & form a new service
10897  Fjo Finish & join another train
10898  Frh Finish & remain here
10899  Fer Finish & exit railway
10900  Frh-sh Finish & repeat shuttle, finally remain here
10901  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
10902  F-nshs Finish & form a shuttle feeder service
10903 */
10904 
10905 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
10906 {
10907  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
10908  // a line that is too long; timetable containing too few lines; and timetable failed to open.
10909  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
10910  // new for v0.2b
10911  // compile ActiveTrackElementNameMap
10912  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
10913 
10915  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
10916  {
10917  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
10919  == Track->ContinuationNameMap.end())
10920  {
10921  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
10922  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
10923  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
10924  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
10925  }
10926  }
10928  // end of new section
10929  std::ifstream TTBLFile(FileName, std::ios_base::binary);
10930 
10931  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
10932  if(TTBLFile.is_open())
10933  {
10934  char *TrainTimetableString = new char[10000];
10935  // enough for over 200 stations, should be adequate!
10936  bool EndOfFile = false;
10937  int Count = 0;
10938  // counts 'relevant' lines, i.e ignores any before the start time on its own line
10939  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10940  // delimiter is '\0' as it's an AnsiString
10941  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10942  // file empty - stores a null in 1st position if doesn't load any characters
10943  {
10944  // may still have eof even if read a line (no CRLF at end), and
10945  // if so need to process it
10946  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
10947  TTBLFile.close();
10948  delete[] TrainTimetableString;
10949  Utilities->CallLogPop(1611);
10950  return(false);
10951  }
10952  AnsiString OneLine(TrainTimetableString);
10953  bool FinalCallFalse = false;
10954  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10955  // get rid of lines before the start time
10956  {
10957  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
10958  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10959  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10960  // stores a null in 1st position if doesn't load any characters
10961  {
10962  // may still have eof even if read a line (no CRLF at end), and
10963  // if so need to process it
10964  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
10965  TTBLFile.close();
10966  delete[] TrainTimetableString;
10967  Utilities->CallLogPop(772);
10968  return(false);
10969  }
10970  OneLine = AnsiString(TrainTimetableString);
10971  }
10972  // here when have accepted the start time
10973  Count++; // increment past the start time
10974  while(!EndOfFile)
10975  {
10976  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10977  // get next line after start time
10978  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10979  // stores a null in 1st position if doesn't load any characters
10980  {
10981  // may still have eof even if read a line (no CRLF at end), and
10982  // if so need to process it
10983  EndOfFile = true;
10984  OneLine = "";
10985  }
10986  else
10987  {
10988  OneLine = AnsiString(TrainTimetableString);
10989  }
10990  if(OneLine.Length() > 9999)
10991  {
10992  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
10993  TTBLFile.close();
10994  delete[] TrainTimetableString;
10995  Utilities->CallLogPop(789);
10996  return(false);
10997  }
10998  bool FinalCallFalse = false;
10999  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
11000  // false for FinalCall - just checking at this stage
11001  {
11002  TTBLFile.close();
11003  delete[] TrainTimetableString;
11004  Utilities->CallLogPop(770);
11005  return(false);
11006  }
11007  if(EndOfFile && (Count < 2))
11008  // Timetable must contain at least two relevant lines, one for start time and at least one train
11009  {
11010  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
11011  TTBLFile.close();
11012  delete[] TrainTimetableString;
11013  Utilities->CallLogPop(771);
11014  return(false);
11015  }
11016  Count++;
11017  }
11018  delete[] TrainTimetableString;
11019  TTBLFile.close();
11020  } // if(TTBLFile.is_open())
11021  else
11022  {
11023  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
11024  Utilities->CallLogPop(2154);
11025  return(false);
11026  }
11027  Utilities->CallLogPop(753);
11028  return(true);
11029 }
11030 
11031 // ---------------------------------------------------------------------------
11032 
11033 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
11034  bool CheckLocationsExistInRailway) // return true for success
11035 
11036 /* Format:
11037  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
11038  descriptive text or anything user wishes
11039  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
11040  be ignored) is taken as the timetable start time.
11041  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
11042  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
11043  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
11044  within the timetable if required.
11045  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
11046  services)
11047  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
11048  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
11049 
11050  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
11051  text timetable file easier
11052 
11053  form:-
11054  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
11055  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
11056  then multiple entries, separated by commas, of the form:-
11057 
11058  Format FormatType
11059  [W]HH:MM;Command (cdt) }TimeCmd }
11060  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
11061  [W]HH:MM;pas;Location }PassTime }
11062  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
11063  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
11064  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
11065  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
11066  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
11067  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
11068  [W]HH:MM;Fns-sh;Details }FSHNewService }
11069  [W]HH:MM;Location (arr & dep) }TimeLoc }
11070  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
11071  Command (Frh only) }FinRemHere }
11072 
11073  R;mm;dd;nn. Repeat Repeat entry
11074 
11075  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
11076  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
11077  at location; or (c) departure time if train already at location (including train started at location either as a new
11078  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
11079  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
11080  minutes, incremental train headcode last 2 digits, and number of repeats.
11081 
11082  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
11083  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
11084  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
11085  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
11086  (it's for a shuttle train to return to depot at end of services)
11087 
11088  Command/Location & details are as follows:-
11089 
11090  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
11091  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
11092  2E44 in its Sfs entry. All these are checked.
11093  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
11094 
11095  Start commands:-
11096  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
11097  with loc as a start entry can't have a location as details)
11098  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
11099  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
11100  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
11101  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
11102  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
11103  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
11104  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
11105  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
11106  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
11107  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
11108  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
11109 
11110  Intermediate commands:-
11111  Time - Location (TimeLoc), can be arrival or departure depending on context
11112  Time Time location (TimeTimeLoc), arrival and departure
11113  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
11114  pas (PassTime), Time;pas;Location
11115  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
11116  joining train's finish details must correspond or the file check will fail
11117  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
11118  new train - that train's starting information must correspond)
11119  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
11120  new train - that train's starting information must correspond)
11121  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
11122 
11123  Finish commands:-
11124  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
11125  creation)
11126  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
11127  shuttle headcode (no train creation)
11128  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
11129  may have to wait for it), details = new headcode (delete train)
11130  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
11131  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
11132  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
11133  here
11134  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
11135  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
11136 
11137  Repeat:-
11138  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
11139  headcodes - it is up to user to avoid duplicates if he/she wishes to.
11140 
11141  Checks carried out with error messages in this function:-
11142  At least one comma in a service line (it's based on a .csv file)
11143  No entries following train information;
11144  At least one comma in remainder after train information (i.e at least a start and a finish entry);
11145  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
11146  First entry not a start entry;
11147  Train information incomplete before a start entry;
11148  Entry follows a finish entry but doesn't begin with 'R';
11149  SplitEntry returns false in a finish entry - message repeats the entry for information;
11150  Last action entry isn't a finish entry.
11151 
11152  Function returns false with no message if:-
11153  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
11154  time is found at all then an error message is given in the calling function);
11155  SplitTrainInfo returns false (message given in called function);
11156  SplitRepeat returns false (message given in called function).
11157 */{
11158  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
11159  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
11160  TTrainDataEntry TempTrainDataEntry;
11161 
11162  EndOfFile = false;
11163  StripSpaces(0, OneLine);
11164  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
11165  // semicolons within the line
11166  ServiceReference = "";
11167  if(OneLine != "")
11168  {
11169  if(OneLine[1] != '*')
11170  {
11171  int SCPos = OneLine.Pos(';');
11172  if(SCPos == 0)
11173  {
11174  ServiceReference = OneLine.SubString(1, 8);
11175  }
11176  else
11177  {
11178  ServiceReference = OneLine.SubString(1, (SCPos - 1));
11179  }
11180  }
11181  }
11182  bool AllCommas = true;
11183 
11184  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
11185  {
11186  if(OneLine[x] != ',')
11187  {
11188  AllCommas = false;
11189  }
11190  }
11191  if(AllCommas || (OneLine == ""))
11192  {
11193  if(Count > 0)
11194  {
11195  EndOfFile = true;
11196  // returns true for a blank line - treated as end of file
11197  Utilities->CallLogPop(1018);
11198  return(true);
11199  }
11200  else // count == 0 so not yet found a start time, no message to be given
11201  {
11202  Utilities->CallLogPop(754);
11203  return(false);
11204  }
11205  }
11206  AnsiString First = "", Second = "", Third = "", Fourth = "";
11207  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
11208  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
11209  TDateTime StartTime(0);
11210  TNumList ExitList;
11211  bool Warning = false;
11212 
11213  if(Count == 0) // no start time found yet
11214  {
11215 /* dropped at v0.6b
11216  AnyHeadCodeValid = false;
11217  if(OneLine.SubString(6,5) == ";0000")
11218  {
11219  AnyHeadCodeValid = true;
11220  }
11221 */
11222  if(!CheckTimeValidity(0, OneLine, StartTime))
11223  {
11224  // no message is given for an invalid time as it's assumed to be an irrelevant line
11225  // if no start time is found at all then an error message is given in the calling function
11226  // AnyHeadCodeValid = false;
11227  Utilities->CallLogPop(755);
11228  return(false);
11229  }
11230  if(FinalCall) // here if start time valid
11231  {
11232  TTClockTime = StartTime;
11233  TimetableStartTime = StartTime;
11234  }
11235  }
11236  else
11237  {
11238  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
11239  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
11240  double MaxBrakeRate = 0;
11241  double PowerAtRail = 0;
11242  int SignallerSpeed = 0;
11243  if(OneLine[1] == '*')
11244  {
11245  Utilities->CallLogPop(1581);
11246  return(true);
11247  // ignore any line beginning with '*' but return true as there is no error
11248  }
11249  int Pos = OneLine.Pos(',');
11250  if(Pos == 0)
11251  {
11252  int SubStringLength = 20;
11253  if(OneLine.Length() < 20)
11254  {
11255  SubStringLength = OneLine.Length();
11256  }
11257  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
11258  Utilities->CallLogPop(766);
11259  return(false);
11260  }
11261  TrainInfoStr = OneLine.SubString(1, Pos - 1);
11262  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
11263  GiveMessages)) // error messages given in SplitTrainInfo
11264  {
11265  Utilities->CallLogPop(773);
11266  return(false);
11267  }
11268  if(FinalCall)
11269  {
11270  // store Train info - conversions done in SplitTrainInfo
11271  // only headcode mandatory for continuing services
11272  TempTrainDataEntry.HeadCode = HeadCode;
11273  TempTrainDataEntry.ServiceReference = HeadCode;
11274  TempTrainDataEntry.Description = Description;
11275  TempTrainDataEntry.StartSpeed = StartSpeed;
11276  TempTrainDataEntry.Mass = Mass;
11277  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
11278  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
11279  TempTrainDataEntry.PowerAtRail = PowerAtRail;
11280  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
11281  TTrainOperatingData TempTrainOperatingData;
11282  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
11283  }
11284  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
11285  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
11286  // so strip them off
11287  while(NewRemainder[NewRemainder.Length()] == ',')
11288  {
11289  if(NewRemainder.Length() > 1)
11290  {
11291  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
11292  }
11293  else
11294  {
11295  NewRemainder = "";
11296  break;
11297  }
11298  }
11299  // check if zero length & fail if so
11300  if(NewRemainder == "")
11301  {
11302  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
11303  Utilities->CallLogPop(769);
11304  return(false);
11305  }
11306  // now have one more entry than there are commas
11307  int CommaCount = 0;
11308  for(int x = 1; x < NewRemainder.Length() + 1; x++)
11309  {
11310  if(NewRemainder[x] == ',')
11311  {
11312  CommaCount++;
11313  }
11314  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
11315  if(CommaCount == 0)
11316  {
11317  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
11318  {
11319  int SubStringLength = 20;
11320  if(OneLine.Length() < 20)
11321  {
11322  SubStringLength = OneLine.Length();
11323  }
11324  TimetableMessage(GiveMessages,
11325  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
11326  OneLine.SubString(1, SubStringLength) + "'....");
11327  Utilities->CallLogPop(783);
11328  return(false);
11329  }
11330  }
11331  AnsiString OneEntry = "";
11332  TTimetableFormatType FormatType;
11333  TTimetableSequenceType SequenceType;
11334  TTimetableLocationType LocationType;
11335  TTimetableShuttleLinkType ShuttleLinkType;
11336  bool FinishFlag = false;
11337  for(int x = 0; x < CommaCount + 1; x++)
11338  {
11339  if((CommaCount == 0) || (x < CommaCount))
11340  // i.e. train entered under signaller control with no repeats, or entry is not the last,
11341  // in which case there's a comma & finish element or repeat still to come this entry could
11342  // be a finish but can't be a repeat
11343  {
11344  if(CommaCount == 0)
11345  {
11346  OneEntry = NewRemainder;
11347  NewRemainder = "";
11348  }
11349  else
11350  {
11351  Pos = NewRemainder.Pos(',');
11352  OneEntry = NewRemainder.SubString(1, Pos - 1);
11353  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
11354  }
11355  First = "";
11356  Second = "";
11357  Third = "";
11358  Fourth = "";
11359  RearStartOrRepeatMins = 0;
11360  FrontStartOrRepeatDigits = 0;
11361  NumberOfRepeats = 0;
11362  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11363  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11364  {
11365  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
11366  Utilities->CallLogPop(756);
11367  return(false);
11368  }
11369  // check if warning for Frh or Fjo & reject
11370  if(Warning && (Second == "Frh"))
11371  {
11372  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
11373  Utilities->CallLogPop(1793);
11374  return(false);
11375  }
11376  if(Warning && (Second == "Fjo"))
11377  {
11378  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11379  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
11380  Utilities->CallLogPop(1794);
11381  return(false);
11382  }
11383  if(x == 0) // should be start event
11384  {
11385  if(SequenceType != Start)
11386  {
11387  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
11388  Utilities->CallLogPop(784);
11389  return(false);
11390  }
11391  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
11392  {
11393  if(NewRemainder[1] != 'R')
11394  {
11395  TimetableMessage(GiveMessages,
11396  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
11397  OneEntry + "'");
11398  Utilities->CallLogPop(787);
11399  return(false);
11400  }
11401  }
11402  if((Second == "Snt") || (Second == "Snt-sh"))
11403  // need full train information including non-default values for at least HeadCode, Description,
11404  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
11405  {
11406  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
11407  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
11408  {
11409  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
11410  OneEntry + "'");
11411  Utilities->CallLogPop(1783);
11412  return(false);
11413  }
11414  }
11415  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
11416  // service continuation - need at least non-default value for HeadCode
11417  {
11418  if(HeadCode == "")
11419  {
11420  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
11421  OneEntry + "'");
11422  Utilities->CallLogPop(788);
11423  return(false);
11424  }
11425  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
11426  {
11427  TimetableMessage(GiveMessages,
11428  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
11429  OneEntry + "'");
11430  Utilities->CallLogPop(843);
11431  return(false);
11432  }
11433  }
11434  }
11435  if(SequenceType == Finish)
11436  {
11437  FinishFlag = true;
11438  // marker for only permitted additional entry being a repeat, only needed if the
11439  // finish entry is not the last entry
11440  }
11441  if(FinalCall)
11442  {
11443  // interpret & add to ActionVector
11444  TDateTime TempTime;
11445  TActionVectorEntry ActionVectorEntry;
11446  ActionVectorEntry.FormatType = FormatType;
11447  ActionVectorEntry.LocationType = LocationType;
11448  ActionVectorEntry.SequenceType = SequenceType;
11449  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11450  ActionVectorEntry.Warning = Warning;
11451  if(FormatType == TimeLoc)
11452  {
11453  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
11454  {
11455  ;
11456  } // these will all be true as final call
11457 
11458  ActionVectorEntry.LocationName = Second;
11459  }
11460  else if(FormatType == PassTime) // new
11461  {
11462  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
11463  {
11464  ;
11465  }
11466  ActionVectorEntry.Command = Second;
11467  ActionVectorEntry.LocationName = Third;
11468  }
11469  else if(FormatType == TimeTimeLoc)
11470  {
11471  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
11472  {
11473  ;
11474  }
11475  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
11476  {
11477  ;
11478  }
11479  ActionVectorEntry.LocationName = Third;
11480  }
11481  else if(FormatType == TimeCmd)
11482  {
11483  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
11484  {
11485  ;
11486  }
11487  ActionVectorEntry.Command = Second;
11488  }
11489  else if(FormatType == ExitRailway)
11490  {
11491  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
11492  {
11493  ;
11494  }
11495  ActionVectorEntry.Command = Second;
11496  ActionVectorEntry.ExitList = ExitList;
11497  }
11498  else if(FormatType == StartNew)
11499  {
11500  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
11501  {
11502  ;
11503  }
11504  ActionVectorEntry.Command = Second;
11505  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11506  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11507  if(Fourth == 'S')
11508  {
11509  ActionVectorEntry.SignallerControl = true;
11510  }
11511  }
11512  else if(FormatType == SNTShuttle)
11513  {
11514  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
11515  {
11516  ;
11517  }
11518  ActionVectorEntry.Command = Second;
11519  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11520  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11521  ActionVectorEntry.OtherHeadCode = Fourth;
11522  }
11523  else if(FormatType == SNSShuttle)
11524  {
11525  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
11526  {
11527  ;
11528  }
11529  ActionVectorEntry.Command = Second;
11530  ActionVectorEntry.OtherHeadCode = Third;
11531  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11532  }
11533  else if(FormatType == TimeCmdHeadCode)
11534  {
11535  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
11536  {
11537  ;
11538  }
11539  ActionVectorEntry.Command = Second;
11540  ActionVectorEntry.OtherHeadCode = Third;
11541  }
11542  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
11543  {
11544  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
11545  {
11546  ;
11547  }
11548  ActionVectorEntry.Command = Second;
11549  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11550  }
11551  else if(FormatType == FSHNewService)
11552  {
11553  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
11554  {
11555  ;
11556  }
11557  ActionVectorEntry.Command = Second;
11558  ActionVectorEntry.OtherHeadCode = Third;
11559  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11560  }
11561  else if(FormatType == FinRemHere)
11562  {
11563  ActionVectorEntry.Command = Second;
11564  }
11565  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11566  }
11567  }
11568  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
11569  {
11570  OneEntry = NewRemainder;
11571  First = "";
11572  Second = "";
11573  Third = "";
11574  Fourth = "";
11575  RearStartOrRepeatMins = 0;
11576  FrontStartOrRepeatDigits = 0;
11577  NumberOfRepeats = 0;
11578  if((FinishFlag) && (OneEntry[1] != 'R'))
11579  // already had a finish entry
11580  {
11581  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
11582  Utilities->CallLogPop(79);
11583  return(false);
11584  }
11585  if(OneEntry[1] != 'R') // must be finish
11586  {
11587  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11588  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11589  {
11590  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
11591  Utilities->CallLogPop(757);
11592  return(false);
11593  }
11594  if(SequenceType != Finish)
11595  {
11596  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
11597  Utilities->CallLogPop(785);
11598  return(false);
11599  }
11600  if(FinalCall)
11601  {
11602  // interpret & add to ActionVector
11603  TDateTime TempTime;
11604  TActionVectorEntry ActionVectorEntry;
11605  ActionVectorEntry.FormatType = FormatType;
11606  ActionVectorEntry.LocationType = LocationType;
11607  ActionVectorEntry.SequenceType = SequenceType;
11608  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11609  ActionVectorEntry.Warning = Warning;
11610  if(FormatType == TimeCmd)
11611  {
11612  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
11613  {
11614  ;
11615  }
11616  ActionVectorEntry.Command = Second;
11617  }
11618  else if(FormatType == TimeCmdHeadCode)
11619  {
11620  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
11621  {
11622  ;
11623  }
11624  ActionVectorEntry.Command = Second;
11625  ActionVectorEntry.OtherHeadCode = Third;
11626  }
11627  else if(FormatType == FNSNonRepeatToShuttle)
11628  {
11629  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
11630  {
11631  ;
11632  }
11633  ActionVectorEntry.Command = Second;
11634  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11635  }
11636  else if(FormatType == FSHNewService)
11637  {
11638  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
11639  {
11640  ;
11641  }
11642  ActionVectorEntry.Command = Second;
11643  ActionVectorEntry.OtherHeadCode = Third;
11644  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11645  }
11646  else if(FormatType == ExitRailway)
11647  {
11648  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
11649  {
11650  ;
11651  }
11652  ActionVectorEntry.Command = Second;
11653  ActionVectorEntry.ExitList = ExitList;
11654  }
11655  else if(FormatType == FinRemHere)
11656  {
11657  ActionVectorEntry.Command = Second;
11658  }
11659  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11660  }
11661  }
11662  else // repeat
11663  {
11664  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
11665  {
11666  Utilities->CallLogPop(786);
11667  // error messages given in SplitRepeat
11668  return(false);
11669  }
11670  if(FinalCall)
11671  {
11672  TActionVectorEntry ActionVectorEntry;
11673  ActionVectorEntry.FormatType = Repeat;
11674  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
11675  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
11676  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
11677  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11678  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11679  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
11680  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11681  }
11682  }
11683  }
11684  }
11685  if(FinalCall)
11686  {
11687  TrainDataVector.push_back(TempTrainDataEntry);
11688  }
11689  }
11690  Utilities->CallLogPop(80);
11691  return(true);
11692 }
11693 
11694 // ---------------------------------------------------------------------------
11695 
11696 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
11697 {
11698  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
11699  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
11700  {
11701  Utilities->CallLogPop(1890);
11702  return(false);
11703  }
11704  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
11705  {
11706  Utilities->CallLogPop(1891);
11707  return(false);
11708  }
11709  Utilities->CallLogPop(1892);
11710  return(true);
11711 }
11712 
11713 // ---------------------------------------------------------------------------
11714 
11715 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
11716 // 1st 5 chars must be HH:MM, anything else will be ignored
11717 {
11718  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
11719  if(TimeStr.Length() < 5)
11720  {
11721  Utilities->CallLogPop(926);
11722  return(false);
11723  }
11724  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
11725  {
11726  Utilities->CallLogPop(927);
11727  return(false);
11728  }
11729  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
11730  {
11731  Utilities->CallLogPop(928);
11732  return(false);
11733  }
11734  if(TimeStr[3] != ':')
11735  {
11736  Utilities->CallLogPop(929);
11737  return(false);
11738  }
11739  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
11740  {
11741  Utilities->CallLogPop(930);
11742  return(false);
11743  }
11744  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
11745  {
11746  Utilities->CallLogPop(931);
11747  return(false);
11748  }
11749  while(TimeStr.Length() > 5)
11750  {
11751  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
11752  }
11753  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
11754  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
11755 
11756  if((WholeHours + FracHour) >= 95.98334)
11757  {
11758  Utilities->CallLogPop(1817);
11759  return(false); // > 95h 59m
11760  }
11761  Time = TDateTime((WholeHours + FracHour) / 24);
11762  Utilities->CallLogPop(932);
11763  return(true);
11764 }
11765 
11766 // ---------------------------------------------------------------------------
11767 
11768 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
11769  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
11770  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
11771 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
11772  Return false for failure.
11773  See description above under ProcessOneTimetableLinefor details of train action entries
11774  NB all types set except LocationType for Sns as may be located or not
11775 */{
11776  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
11777  Warning = false;
11778  TDateTime TempTime;
11779 
11780  if(OneEntry.Length() > 0)
11781  {
11782  if(OneEntry[1] == 'W') // warning
11783  {
11784  Warning = true;
11785  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
11786  // strip it off
11787  }
11788  }
11789  if(OneEntry == "Frh")
11790  {
11791  FormatType = FinRemHere;
11792  SequenceType = Finish;
11793  LocationType = AtLocation;
11794  ShuttleLinkType = NotAShuttleLink;
11795  Second = "Frh";
11796  Utilities->CallLogPop(1016);
11797  return(true);
11798  }
11799  if(OneEntry.Length() < 7)
11800  {
11801  Utilities->CallLogPop(907);
11802  return(false); // 'HH:MM;' + at least a one-letter location name
11803  }
11804  int Pos = OneEntry.Pos(';'); // first segment delimiter
11805 
11806  if(Pos != 6)
11807  {
11808  Utilities->CallLogPop(908);
11809  return(false);
11810  // no delimiter or delimiter not in position 6, has to be a time so fail
11811  }
11812  First = OneEntry.SubString(1, 5); // has to be a time
11813  if(!CheckTimeValidity(16, First, TempTime))
11814  {
11815  Utilities->CallLogPop(909);
11816  return(false);
11817  }
11818  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
11819 
11820  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
11821  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
11822  {
11823  if(Remainder.Length() < 7)
11824  {
11825  Utilities->CallLogPop(910);
11826  return(false); // 'HH:MM;' + at least a one-letter location name
11827  }
11828  Pos = Remainder.Pos(';'); // second segment delimiter
11829  if(Pos == 0)
11830  {
11831  Utilities->CallLogPop(911);
11832  return(false);
11833  // no delimiter, has to be one between departure time & location
11834  }
11835  Second = Remainder.SubString(1, 5); // has to be a time
11836  if(!CheckTimeValidity(15, Second, TempTime))
11837  {
11838  Utilities->CallLogPop(912);
11839  return(false);
11840  }
11841  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11842  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
11843  {
11844  Utilities->CallLogPop(913);
11845  return(false);
11846  }
11847  FormatType = TimeTimeLoc;
11848  SequenceType = Intermediate;
11849  LocationType = AtLocation;
11850  ShuttleLinkType = NotAShuttleLink;
11851  Utilities->CallLogPop(914);
11852  return(true);
11853  }
11854  Pos = Remainder.Pos(';'); // second segment delimiter
11855  if(Pos == 0) // no third segment so second must be a location, or cdt
11856  {
11857  Second = Remainder;
11858  if(Second == "cdt")
11859  {
11860  FormatType = TimeCmd;
11861  ShuttleLinkType = NotAShuttleLink;
11862  LocationType = AtLocation;
11863  SequenceType = Intermediate;
11864  Utilities->CallLogPop(915);
11865  return(true);
11866  }
11867  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
11868  {
11869  Utilities->CallLogPop(916);
11870  return(false);
11871  }
11872  else
11873  {
11874  FormatType = TimeLoc;
11875  LocationType = AtLocation;
11876  SequenceType = Intermediate;
11877  ShuttleLinkType = NotAShuttleLink;
11878  Utilities->CallLogPop(917);
11879  return(true);
11880  }
11881  }
11882  // here if second segment is a command, with a third & maybe fourth segments as details
11883  if((Pos != 4) && (Pos != 7) && (Pos != 8))
11884  {
11885  Utilities->CallLogPop(918);
11886  return(false);
11887  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
11888  }
11889  Second = Remainder.SubString(1, Pos - 1); // command
11890 
11891  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11892  // details
11893  Pos = Remainder.Pos(';'); // third segment delimiter
11894  if(Pos == 0)
11895  {
11896  Third = Remainder;
11897  }
11898  else
11899  {
11900  Third = Remainder.SubString(1, Pos - 1);
11901  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11902  }
11903  if((Second == "Snt") || (Second == "Snt-sh"))
11904  // third has to be 2 element idents with a space between
11905  {
11906  int SpacePos = Third.Pos(' ');
11907  if(SpacePos == 0)
11908  {
11909  Utilities->CallLogPop(919);
11910  return(false); // no space
11911  }
11912  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
11913  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
11914  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
11915  if(CheckLocationsExistInRailway)
11916  {
11917  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
11918  {
11919  Utilities->CallLogPop(920);
11920  return(false);
11921  }
11922  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
11923  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
11924  }
11925  if(Second == "Snt")
11926  {
11927  FormatType = StartNew;
11928  SequenceType = Start;
11929  LocationType = NoLocation;
11930  // can't be set until know whether located or not - done in SecondPassActions
11931  ShuttleLinkType = NotAShuttleLink;
11932  }
11933  else // Snt-sh
11934  {
11935  FormatType = SNTShuttle;
11936  LocationType = AtLocation;
11937  SequenceType = Start;
11938  ShuttleLinkType = ShuttleLink;
11939  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
11940  {
11941  Utilities->CallLogPop(1038);
11942  return(false);
11943  }
11944  }
11945  Utilities->CallLogPop(921);
11946  return(true);
11947  }
11948  if(Second == "Sns-sh") // third & fourth have to be headcodes
11949  {
11950  FormatType = SNSShuttle;
11951  LocationType = AtLocation;
11952  SequenceType = Start;
11953  ShuttleLinkType = ShuttleLink;
11954  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
11955  {
11956  Utilities->CallLogPop(1039);
11957  return(false);
11958  }
11959  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
11960  {
11961  Utilities->CallLogPop(1040);
11962  return(false);
11963  }
11964  Utilities->CallLogPop(1041);
11965  return(true);
11966  }
11967  if(Second == "F-nshs")
11968  {
11969  FormatType = FNSNonRepeatToShuttle;
11970  LocationType = AtLocation;
11971  SequenceType = Finish;
11972  ShuttleLinkType = ShuttleLink;
11973  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
11974  {
11975  Utilities->CallLogPop(1047);
11976  return(false);
11977  }
11978  Utilities->CallLogPop(1048);
11979  return(true);
11980  }
11981  if(Second == "Sns-fsh")
11982  {
11983  FormatType = SNSNonRepeatFromShuttle;
11984  LocationType = AtLocation;
11985  SequenceType = Start;
11986  ShuttleLinkType = ShuttleLink;
11987  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
11988  {
11989  Utilities->CallLogPop(1098);
11990  return(false);
11991  }
11992  Utilities->CallLogPop(1099);
11993  return(true);
11994  }
11995  if(Second == "Fns-sh") // third & fourth have to be headcodes
11996  {
11997  FormatType = FSHNewService;
11998  LocationType = AtLocation;
11999  SequenceType = Finish;
12000  ShuttleLinkType = ShuttleLink;
12001  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
12002  {
12003  Utilities->CallLogPop(1050);
12004  return(false);
12005  }
12006  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
12007  {
12008  Utilities->CallLogPop(1051);
12009  return(false);
12010  }
12011  Utilities->CallLogPop(1052);
12012  return(true);
12013  }
12014  // new segment for 'pas'
12015  if(Second == "pas") // third has to be a location
12016  {
12017  FormatType = PassTime;
12018  LocationType = EnRoute;
12019  SequenceType = Intermediate;
12020  ShuttleLinkType = NotAShuttleLink;
12021  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
12022  {
12023  Utilities->CallLogPop(1515);
12024  return(false);
12025  }
12026  Utilities->CallLogPop(1516);
12027  return(true);
12028  }
12029  // new segment for revised 'Fer'
12030  if(Second == "Fer")
12031  // third has to be a set of IDs separated by spaces, and at least 1
12032  {
12033  FormatType = ExitRailway;
12034  LocationType = EnRoute;
12035  SequenceType = Finish;
12036  ShuttleLinkType = NotAShuttleLink;
12037  if(CheckLocationsExistInRailway)
12038  {
12039  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
12040  {
12041  Utilities->CallLogPop(1519);
12042  return(false);
12043  }
12044  }
12045  Utilities->CallLogPop(1520);
12046  return(true);
12047  }
12048  // all remainder must be TimeCmdHeadCode types to be valid
12049  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
12050  (Second != "Frh-sh"))
12051  {
12052  Utilities->CallLogPop(922);
12053  return(false); // all TimeCmdHeadCode types
12054  }
12055  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
12056  {
12057  Utilities->CallLogPop(923);
12058  return(false);
12059  }
12060  FormatType = TimeCmdHeadCode;
12061  LocationType = AtLocation;
12062  if(Second == "Frh-sh")
12063  {
12064  ShuttleLinkType = ShuttleLink;
12065  }
12066  else
12067  {
12068  ShuttleLinkType = NotAShuttleLink;
12069  }
12070  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
12071  {
12072  SequenceType = Finish;
12073  }
12074  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
12075  {
12076  SequenceType = Intermediate;
12077  }
12078  if((Second == "Sfs") || (Second == "Sns"))
12079  {
12080  SequenceType = Start;
12081  }
12082  Utilities->CallLogPop(924);
12083  return(true);
12084 }
12085 
12086 // ---------------------------------------------------------------------------
12087 
12088 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
12089 {
12090  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
12091  // and contains no special characters
12092  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
12093  if(LocStr == "")
12094  {
12095  Utilities->CallLogPop(1353);
12096  return(false); // has to have at least one character
12097  }
12098  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
12099  {
12100  Utilities->CallLogPop(1354);
12101  return(false); // can't begin with a number
12102  }
12103  for(int x = 1; x < LocStr.Length() + 1; x++)
12104  {
12105  if(LocStr[x] < ' ')
12106  {
12107  Utilities->CallLogPop(1355);
12108  return(false); // contains a special character
12109  }
12110  if(LocStr[x] > 'z')
12111  {
12112  Utilities->CallLogPop(1356);
12113  return(false); // contains a character outside the standard ASCII set
12114  }
12115  }
12116  // check exists in railway location list if CheckLocationsExistInRailway is true
12117  if(CheckLocationsExistInRailway)
12118  {
12119  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
12120  {
12121  TimetableMessage(GiveMessages, "Location name '" + LocStr +
12122  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
12123  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
12124  "that includes a continuation will not be valid.");
12125  Utilities->CallLogPop(1357);
12126  return(false);
12127  }
12128  }
12129  Utilities->CallLogPop(1358);
12130  return(true);
12131 }
12132 
12133 // ---------------------------------------------------------------------------
12134 
12135 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
12136 {
12137  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
12138  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
12139  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
12140  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
12141  HeadCode);
12142  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
12143  {
12144  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
12145  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Sns, Snt-sh, Sns-sh, Fns, Fns-sh or Frh-sh");
12146  Utilities->CallLogPop(1359);
12147  return(false);
12148  }
12149  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
12150  for(int x = 1; x < (HeadCode.Length() + 1); x++)
12151  {
12152  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
12153  {
12154  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
12155  Utilities->CallLogPop(1895);
12156  return(false);
12157  }
12158  }
12159  // secondly ensure the true Headcode only has letters or digits
12160  for(int x = 3; x >= 0; x--)
12161  {
12162  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
12163  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
12164  {
12165  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
12166  Utilities->CallLogPop(1790);
12167  return(false);
12168  }
12169  }
12170  Utilities->CallLogPop(1364);
12171  return(true);
12172 }
12173 
12174 // ---------------------------------------------------------------------------
12175 
12176 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
12177 // set of track element IDs, separated by spaces, and at least 1 present
12178 {
12179  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndPopulateListOfIDs," + IDSet); //had wrong title, changed at v2.13.0
12180  ExitList.clear();
12181  AnsiString CurrentID = "";
12182 
12183  if(IDSet.Length() == 0)
12184  {
12185  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
12186  Utilities->CallLogPop(1521);
12187  return(false);
12188  }
12189  for(int x = 1; x <= IDSet.Length(); x++)
12190  {
12191  char C = IDSet[x];
12192  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
12193  {
12194  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
12195  Utilities->CallLogPop(1522);
12196  return(false);
12197  }
12198 /* don't use, error checks in GetTrackVectorPositionFromString instead
12199  if(C == '-') //this section added at v2.13.0 because of Amon Sadler's error file submitted 24/03/22
12200  {
12201  if((x==1) || (x == IDSet.Length()))
12202  {
12203  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
12204  Utilities->CallLogPop(2479);
12205  return(false);
12206  }
12207  if((IDSet[x-1] < '0') || (IDSet[x-1] > '9') || (IDSet[x+1] < '0') || (IDSet[x+1] > '9'))
12208  {
12209  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
12210  Utilities->CallLogPop(2480);
12211  return(false);
12212  }
12213  }
12214 */
12215  }
12216  int Pos = IDSet.Pos(' '); // look for the first space
12217 
12218  while(true)
12219  {
12220  if(Pos == 0)
12221  {
12222  CurrentID = IDSet;
12223  IDSet = "";
12224  }
12225  else
12226  {
12227  CurrentID = IDSet.SubString(1, Pos - 1);
12228  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
12229  }
12230  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
12231  if(VecPos == -1)
12232  {
12233  Utilities->CallLogPop(1523);
12234  return(false); // messages given in GetTrackVectorPositionFromString
12235  }
12236  else
12237  {
12238  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
12239  {
12240  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
12241  Utilities->CallLogPop(1524);
12242  return(false);
12243  }
12244  else
12245  {
12246  // first check for duplicates
12247  if(!ExitList.empty())
12248  {
12249  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
12250  {
12251  if(*ELIT == VecPos)
12252  {
12253  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
12254  Utilities->CallLogPop(1532);
12255  return(false);
12256  }
12257  }
12258  }
12259  // of OK add it to the list
12260  ExitList.push_back(VecPos);
12261  }
12262  }
12263  if(IDSet == "")
12264  {
12265  Utilities->CallLogPop(1525);
12266  return(true);
12267  }
12268  else
12269  {
12270  Pos = IDSet.Pos(' '); // look for the next space
12271  }
12272  } // while(true)
12273 }
12274 
12275 // ---------------------------------------------------------------------------
12276 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
12277  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
12278 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
12279 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
12280 // of each item
12281 {
12282  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
12283  int Pos = 0;
12284  AnsiString Remainder = "";
12285  int SemiColonCount = 0;
12286 
12287  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
12288  {
12289  if(TrainInfoStr[x] == ';')
12290  {
12291  SemiColonCount++;
12292  }
12293  }
12294  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
12295  {
12296  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
12297  "'. Should be headcode + optional description for a continuing service;" +
12298  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
12299  Utilities->CallLogPop(880);
12300  return(false);
12301  }
12302  if(SemiColonCount == 0)
12303  {
12304  HeadCode = TrainInfoStr;
12305  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
12306  {
12307  Utilities->CallLogPop(881);
12308  return(false);
12309  }
12310  Utilities->CallLogPop(882);
12311  return(true);
12312  }
12313  if(SemiColonCount == 1) // headcode & description only
12314  {
12315  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
12316  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
12317  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
12318  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
12319  {
12320  Utilities->CallLogPop(883);
12321  return(false);
12322  }
12323  if(Description == "")
12324  {
12325  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
12326  Utilities->CallLogPop(884);
12327  return(false);
12328  }
12329  if(Description.Length() > 60)
12330  {
12331  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
12332  Utilities->CallLogPop(1157);
12333  return(false);
12334  }
12335  for(int x = 1; x < Description.Length() + 1; x++)
12336  {
12337  if((Description[x] < ' ') || (Description[x] > '~'))
12338  {
12339  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
12340  Utilities->CallLogPop(885);
12341  return(false);
12342  }
12343  }
12344  Utilities->CallLogPop(886);
12345  return(true);
12346  }
12347  // if here must have 6 or 7 semicolons
12348  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
12349  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
12350  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
12351  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
12352  {
12353  Utilities->CallLogPop(887);
12354  return(false);
12355  }
12356  Pos = Remainder.Pos(';'); // 2nd delimiter
12357  Description = Remainder.SubString(1, Pos - 1);
12358  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12359  if(Description == "")
12360  {
12361  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
12362  Utilities->CallLogPop(888);
12363  return(false);
12364  }
12365  if(Description.Length() > 60)
12366  {
12367  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
12368  Utilities->CallLogPop(1158);
12369  return(false);
12370  }
12371  for(int x = 1; x < Description.Length() + 1; x++)
12372  {
12373  if((Description[x] < ' ') || (Description[x] > 126))
12374  {
12375  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
12376  Utilities->CallLogPop(889);
12377  return(false);
12378  }
12379  }
12380  Pos = Remainder.Pos(';'); // 3rd delimiter
12381  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
12382 
12383  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12384  if(StartSpeedStr == "")
12385  {
12386  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
12387  Utilities->CallLogPop(890);
12388  return(false);
12389  }
12390  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
12391  {
12392  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
12393  {
12394  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
12395  Utilities->CallLogPop(891);
12396  return(false);
12397  }
12398  }
12399  StartSpeed = StartSpeedStr.ToInt();
12400  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
12401  {
12402  StartSpeed = TTrain::MaximumSpeedLimit;
12403  if(!SSHigh) // added at v2.4.0
12404  {
12405  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12406  SSHigh = true;
12407  }
12408  }
12409  Pos = Remainder.Pos(';'); // 4th delimiter
12410  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
12411 
12412  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12413  if(MaxRunningSpeedStr == "")
12414  {
12415  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
12416  Utilities->CallLogPop(892);
12417  return(false);
12418  }
12419  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
12420  {
12421  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
12422  {
12423  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
12424  Utilities->CallLogPop(893);
12425  return(false);
12426  }
12427  }
12428  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
12429  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
12430  {
12431  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
12432  if(!MRSHigh) // added at v2.4.0
12433  {
12434  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12435  MRSHigh = true;
12436  }
12437  }
12438  if(MaxRunningSpeed < 10)
12439  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
12440  {
12441  MaxRunningSpeed = 10;
12442  if(!MRSLow) // added at v2.4.0
12443  {
12444  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
12445  MRSLow = true;
12446  }
12447  }
12448  Pos = Remainder.Pos(';'); // 5th delimiter
12449  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
12450 
12451  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12452  if(MassStr == "")
12453  {
12454  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
12455  Utilities->CallLogPop(895);
12456  return(false);
12457  }
12458  for(int x = 1; x < MassStr.Length() + 1; x++)
12459  {
12460  if((MassStr[x] < '0') || (MassStr[x] > '9'))
12461  {
12462  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
12463  Utilities->CallLogPop(896);
12464  return(false);
12465  }
12466  }
12467  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
12468  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
12469  {
12470  Mass = TTrain::MaximumMassLimit;
12471  if(!MassHigh) // added at v2.4.0
12472  {
12473  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
12474  MassHigh = true;
12475  }
12476  }
12477  if(Mass == 0)
12478  {
12479  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
12480  Utilities->CallLogPop(897);
12481  return(false);
12482  }
12483  Pos = Remainder.Pos(';'); // 6th delimiter
12484  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
12485 
12486  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12487  if(MaxBrakeForceStr == "")
12488  {
12489  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
12490  Utilities->CallLogPop(898);
12491  return(false);
12492  }
12493  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
12494  {
12495  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
12496  {
12497  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
12498  Utilities->CallLogPop(899);
12499  return(false);
12500  }
12501  }
12502  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
12503 
12504  // convert to kg force
12505  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
12506  {
12507  MaxBrakeForce = Mass;
12508  if(!BFHigh) // added at v2.4.0
12509  {
12510  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
12511  BFHigh = true;
12512  }
12513  }
12514  if((MaxBrakeForce / Mass) < 0.01)
12515  {
12516  MaxBrakeForce = Mass * 0.01;
12517  if(!BFLow) // added at v2.4.0
12518  {
12519  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
12520  BFLow = true;
12521  }
12522  }
12523  // convert to m/s/s
12524  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
12525  // now may have just a power entry or power and signaller max. speed
12526  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
12527 
12528  if(SemiColonCount == 6)
12529  {
12530  GrossPowerStr = Remainder;
12531  SignallerSpeedStr = "30"; // default value
12532  }
12533  else // must be 7
12534  {
12535  Pos = Remainder.Pos(';'); // 7th delimiter
12536  GrossPowerStr = Remainder.SubString(1, Pos - 1);
12537  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12538  }
12539  // deal with GrossPower
12540  if(GrossPowerStr == "")
12541  {
12542  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
12543  Utilities->CallLogPop(901);
12544  return(false);
12545  }
12546  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
12547  {
12548  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
12549  {
12550  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
12551  Utilities->CallLogPop(902);
12552  return(false);
12553  }
12554  }
12555 
12556  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
12557 
12558  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
12559  {
12560  GrossPower = TTrain::MaximumPowerLimit;
12561  if(!PwrHigh)
12562  {
12563  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
12564  PwrHigh = true;
12565  }
12566  }
12567  else if(GrossPower == 0) // changed at v2.4.0
12568  {
12569  GrossPower = 0.1;
12570  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
12571  }
12572  else if((GrossPower > 0) && (GrossPower < 10000))
12573  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
12574  {
12575  GrossPower = 10000;
12576  }
12577  PowerAtRail = GrossPower * 0.8;
12578  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
12579 
12580  // deal with SignallerSpeed
12581  if(SignallerSpeedStr == "")
12582  {
12583  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
12584  Utilities->CallLogPop(1771);
12585  return(false);
12586  }
12587  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
12588  {
12589  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
12590  {
12591  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
12592  Utilities->CallLogPop(1769);
12593  return(false);
12594  }
12595  }
12596  SignallerSpeed = SignallerSpeedStr.ToInt();
12597  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
12598  {
12599  SignallerSpeed = TTrain::MaximumSpeedLimit;
12600  if(!SigSHigh)
12601  {
12602  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12603  SigSHigh = true;
12604  }
12605  }
12606  if(SignallerSpeed < 10)
12607  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
12608  {
12609  SignallerSpeed = 10;
12610  if(!SigSLow)
12611  {
12612  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
12613  SigSLow = true;
12614  }
12615  // Utilities->CallLogPop(1770);
12616  // return false;
12617  }
12618  Utilities->CallLogPop(904);
12619  return(true);
12620 }
12621 
12622 // ---------------------------------------------------------------------------
12623 
12624 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
12625  bool GiveMessages)
12626 {
12627  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
12628  // function checks validity of each item and returns false for error
12629  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
12630  if(OneEntry.Length() < 7)
12631  {
12632  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12633  Utilities->CallLogPop(865);
12634  return(false);
12635  }
12636  int SemiColonCount = 0;
12637 
12638  for(int x = 1; x < OneEntry.Length() + 1; x++)
12639  {
12640  if(OneEntry[x] == ';')
12641  {
12642  SemiColonCount++;
12643  }
12644  }
12645  if(SemiColonCount != 3)
12646  {
12647  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12648  Utilities->CallLogPop(866);
12649  return(false);
12650  }
12651  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
12652  {
12653  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12654  Utilities->CallLogPop(867);
12655  return(false);
12656  }
12657  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
12658  // strip off R;
12659 
12660  int Pos = 0;
12661 
12662  Pos = Remainder.Pos(';');
12663  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
12664 
12665  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12666  if(MinutesStr == "")
12667  {
12668  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
12669  Utilities->CallLogPop(868);
12670  return(false);
12671  }
12672  if(MinutesStr.Length() > 3)
12673  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
12674  {
12675  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
12676  Utilities->CallLogPop(2119);
12677  return(false);
12678  }
12679  for(int x = 1; x < MinutesStr.Length() + 1; x++)
12680  {
12681  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
12682  {
12683  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
12684  Utilities->CallLogPop(869);
12685  return(false);
12686  }
12687  }
12688  RearStartOrRepeatMins = MinutesStr.ToInt();
12689  if(RearStartOrRepeatMins == 0)
12690  {
12691  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
12692  Utilities->CallLogPop(870);
12693  return(false);
12694  }
12695  Pos = Remainder.Pos(';');
12696  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
12697 
12698  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12699  if(DigitsStr == "")
12700  {
12701  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
12702  Utilities->CallLogPop(871);
12703  return(false);
12704  }
12705  for(int x = 1; x < DigitsStr.Length() + 1; x++)
12706  {
12707  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
12708  {
12709  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
12710  Utilities->CallLogPop(872);
12711  return(false);
12712  }
12713  }
12714  if(DigitsStr.Length() > 2)
12715  {
12716  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
12717  Utilities->CallLogPop(873);
12718  return(false);
12719  }
12720  FrontStartOrRepeatDigits = DigitsStr.ToInt();
12721 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
12722  route rather than the service
12723  if(FrontStartOrRepeatDigits == 0)
12724  {
12725  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
12726  Utilities->CallLogPop(874);
12727  return false;
12728  }
12729 */
12730  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
12731  // new for v0.6b for unrestricted headcodes
12732  {
12733  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
12734  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
12735  Utilities->CallLogPop(1889);
12736  return(false);
12737  }
12738  AnsiString NumberStr = Remainder;
12739 
12740  if(NumberStr == "")
12741  {
12742  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
12743  Utilities->CallLogPop(875);
12744  return(false);
12745  }
12746  if(NumberStr.Length() > 4)
12747  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
12748  {
12749  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
12750  Utilities->CallLogPop(2118);
12751  return(false);
12752  }
12753  for(int x = 1; x < NumberStr.Length() + 1; x++)
12754  {
12755  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
12756  // catches negative numbers
12757  {
12758  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
12759  Utilities->CallLogPop(876);
12760  return(false);
12761  }
12762  }
12763  NumberOfRepeats = NumberStr.ToInt();
12764  if(NumberOfRepeats == 0)
12765  {
12766  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
12767  Utilities->CallLogPop(877);
12768  return(false);
12769  }
12770  Utilities->CallLogPop(878);
12771  return(true);
12772 }
12773 
12774 // ---------------------------------------------------------------------------
12775 
12776 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
12777 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
12778  vector rather than the timetable
12779  Note also that for unlocated Snt entries the LocationType hasn't yet been set
12780 
12781  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
12782 
12783  For info:-
12784  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
12785  {
12786  public:
12787  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled event entries, null
12789  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
12790  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
12791  int NumberOfRepeats; ///< the number of repeating services
12792  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
12794  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
12796  TNumList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
12797  TTimetableFormatType FormatType; ///< defines the timetable action type
12798  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
12799  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
12800  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
12801  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
12803  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
12805 
12806  // inline function
12807 
12809  TActionVectorEntry() {
12810  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
12811  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
12812  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
12813  Warning = false; SignallerControl = false;
12814  }
12815  };
12816 
12817  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
12818 
12819  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
12820  {
12821  public:
12822  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
12825  double MaxBrakeRate; ///< in metres/sec/sec
12826  double MaxRunningSpeed; ///< in km/h
12827  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
12828  int Mass; ///< in kg
12829  int NumberOfTrains; ///< number of repeats + 1
12830  int SignallerSpeed; ///< in km/h for use when under signaller control
12831  int StartSpeed; ///< in km/h
12832  TActionVector ActionVector; ///< all the actions for the train
12833  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
12834 
12835  //inline function
12836 
12838  TTrainDataEntry()
12839  {
12840  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
12841  }
12842  };
12843 
12844  Allowable successors:-
12845  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
12846  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
12847  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
12848  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12849  set location, else fails)
12850  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12851  set location, else fails)
12852  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12853  set location, else fails)
12854  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12855  set location, else fails)
12856  Fns -> R only
12857  F-nshs -> Nothing (no repeats permitted)
12858  Fjo -> R only
12859  Frh -> R only
12860  Fer -> R only
12861  Frh-sh -> R only
12862  Fns-sh -> R only
12863  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
12864  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12865  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12866  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12867  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12868  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12869  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12870  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12871  Repeat -> Nothing
12872 
12873  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
12874  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
12875  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
12876  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
12877  Check all times increase or stay same through ActionVector
12878  Cycle through all entries in vector setting arr & dep times based on above list
12879  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
12880  Check locations match the arr & dep TimeLoc entries
12881  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
12882  Make above valid succession checks
12883  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
12884  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
12885  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
12886  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
12887  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
12888  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
12889  Set train info for Sfs & Sns entries
12890  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
12891  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
12892  element at each end, or length of 3 & 1 extra element at either end
12893  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
12894  Check all Cmds have EventTime set & Arr & Dep times = -1
12895  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
12896  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
12897 
12898  Give messages in function if errors detected and clear the vector. Return false for failure.
12899 */
12900 
12901 /* Earlier checks:-
12902  Checks carried out with error messages in this function:-
12903  At least one comma in the line (it's based on a csv file);
12904  No entries following train information;
12905  At least one comma in remainder after train information (i.e at least a start and a finish entry);
12906  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
12907  First entry not a start entry;
12908  Train information incomplete before a start entry;
12909  Entry follows a finish entry but doesn't begin with 'R';
12910  SplitEntry returns false in a finish entry - message repeats the entry for information;
12911  Last action entry isn't a finish entry.
12912 
12913  Function returns false with no message if:-
12914  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
12915  time is found at all then an error message is given in the calling function);
12916  SplitTrainInfo returns false (message given in called function);
12917  SplitRepeat returns false (message given in called function).
12918 
12919 Double crosslink (shuttle) table: [OtherHeadCode, NonRepeatingShuttleLinkHeadCode, LinkedTrainEntryPtr, NonRepeatingShuttleLinkEntryPtr] <-- these for easier searching for this table
12920 
12921 Command Format OtherHead NonRepeating- LinkedTrain- NonRepeating- Decsription
12922  Code ShuttleLink- EntryPtr ShuttleLink
12923  HeadCode EntryPtr
12924 
12925 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
12926 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
12927 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
12928 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
12929 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
12930 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
12931 
12932 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
12933 
12934 */{
12935  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
12936  if(TrainDataVector.empty())
12937  {
12938  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
12939  TrainDataVector.clear();
12940  Utilities->CallLogPop(1832);
12941  return(false);
12942  }
12943 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
12944  1) must have at least one actionvector entry
12945  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
12946  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
12947  4) first entry must be a start;
12948  4a) if first entry is Snt and not signallercontrol and second is a finish then it must be at a location with zero start speed
12949  5) a start must be the first entry;
12950  6) a repeat entry must be the last;
12951  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
12952  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
12953  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
12954 */
12955 
12956  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
12957  TwoLocationFlag = false; //added at v2.9.1
12958  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
12959  {
12960  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12961  if(TrainDataVector.at(x).ActionVector.empty())
12962  {
12963  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
12964  TrainDataVector.clear();
12965  Utilities->CallLogPop(1833);
12966  return(false);
12967  }
12968  }
12969  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
12970  {
12971  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12972  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12973  if(!(AVEntry0.SignallerControl))
12974  {
12975  if(TrainDataVector.at(x).ActionVector.size() == 1)
12976  {
12977  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
12978  TrainDataVector.clear();
12979  Utilities->CallLogPop(1822);
12980  return(false);
12981  }
12982  }
12983  }
12984  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
12985  {
12986  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12987  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12988  if(AVEntry0.SignallerControl)
12989  {
12990  if(TrainDataVector.at(x).ActionVector.size() > 2)
12991  {
12992  SecondPassMessage(GiveMessages,
12993  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
12994  TDEntry.HeadCode);
12995  TrainDataVector.clear();
12996  Utilities->CallLogPop(1837);
12997  return(false);
12998  }
12999  if(TrainDataVector.at(x).ActionVector.size() > 1)
13000  {
13001  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13002  if(AVEntry1.FormatType != Repeat)
13003  {
13004  SecondPassMessage(GiveMessages,
13005  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
13006  TrainDataVector.clear();
13007  Utilities->CallLogPop(1838);
13008  return(false);
13009  }
13010  }
13011  }
13012  }
13013  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
13014  {
13015  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13016  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13017  if(AVEntry0.SequenceType != Start)
13018  {
13019  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
13020  TrainDataVector.clear();
13021  Utilities->CallLogPop(1824);
13022  return(false);
13023  }
13024  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
13025  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
13026  // and others for a located Snt, but those checks done later
13027  {
13028  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13029  // must be a second entry if first not signallercontrol
13030  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
13031  {
13032  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
13033  TDEntry.HeadCode);
13034  TrainDataVector.clear();
13035  Utilities->CallLogPop(2046);
13036  return(false);
13037  }
13038  }
13039  }
13040  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
13041  {
13042  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13043  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13044  {
13045  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13046  if((AVEntry.SequenceType == Start) && (y != 0))
13047  {
13048  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
13049  TrainDataVector.clear();
13050  Utilities->CallLogPop(1825);
13051  return(false);
13052  }
13053  }
13054  }
13055  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
13056  {
13057  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13058  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13059  {
13060  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13061  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
13062  {
13063  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
13064  TrainDataVector.clear();
13065  Utilities->CallLogPop(1826);
13066  return(false);
13067  }
13068  }
13069  }
13070  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
13071  {
13072  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13073  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13074  {
13075  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13076  if((y == 0) && AVEntry.SignallerControl)
13077  {
13078  break;
13079  }
13080  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
13081  {
13082  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
13083  {
13084  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
13085  TrainDataVector.clear();
13086  Utilities->CallLogPop(1827);
13087  return(false);
13088  }
13089  if(AVEntry.FormatType == Repeat)
13090  {
13091  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
13092  if(LastAVEntry.SequenceType != Finish)
13093  {
13094  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
13095  TrainDataVector.clear();
13096  Utilities->CallLogPop(1828);
13097  return(false);
13098  }
13099  }
13100  }
13101  }
13102  }
13103  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
13104  {
13105  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13106  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13107  {
13108  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13109  if(AVEntry.SequenceType == Finish)
13110  {
13111  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
13112  {
13113  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
13114  TrainDataVector.clear();
13115  Utilities->CallLogPop(1829);
13116  return(false);
13117  }
13118  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
13119  {
13120  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
13121  {
13122  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
13123  TDEntry.HeadCode);
13124  TrainDataVector.clear();
13125  Utilities->CallLogPop(1830);
13126  return(false);
13127  }
13128  }
13129  }
13130  }
13131  }
13132 
13133  // end of new preliminary checks
13134 
13135  // check ActionVector present and check start event successor validity
13136  // For Snt & Snt-sh set location if stopped, don't set any times yet
13137  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13138  {
13139  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13140  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13141  // use reference so can change internals where necessary
13142  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
13143  {
13144  AnsiString LocationName = "";
13145  if(IsSNTEntryLocated(0, TDEntry, LocationName))
13146  // it is at a location
13147  {
13148  if(TDEntry.StartSpeed == 0) // stopped
13149  {
13150  AVEntry0.LocationName = LocationName;
13151  AVEntry0.LocationType = AtLocation;
13152  // check successor validity for located Snt that isn't a SignallerControl entry
13153  if(!AVEntry0.SignallerControl)
13154  {
13155  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13156  // at least 2 entries present checked in integrity check so (1) valid
13157  if(!AtLocSuccessor(AVEntry1))
13158  {
13159  // Frh following Snt-sh will return false in location check, so no need to check here
13160  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
13161  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
13162  TrainDataVector.clear();
13163  Utilities->CallLogPop(523);
13164  return(false);
13165  }
13166  }
13167  }
13168  else
13169  {
13170  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
13171  TDEntry.HeadCode);
13172  TrainDataVector.clear();
13173  Utilities->CallLogPop(791);
13174  return(false);
13175  }
13176  }
13177  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
13178  {
13179  if(AVEntry0.Command == "Snt-sh")
13180  {
13181  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
13182  TrainDataVector.clear();
13183  Utilities->CallLogPop(1042);
13184  return(false);
13185  }
13186  AVEntry0.LocationType = EnRoute;
13187  if(!AVEntry0.SignallerControl)
13188  {
13189  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13190  // at least 2 entries checked in integrity check so (1) valid
13191  if(!MovingSuccessor(AVEntry1))
13192  {
13193  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
13194  TDEntry.HeadCode);
13195  TrainDataVector.clear();
13196  Utilities->CallLogPop(790);
13197  return(false);
13198  }
13199  }
13200  }
13201  }
13202  // check other start successors
13203  else if(AVEntry0.SequenceType == Start)
13204  {
13205  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13206  // at least 2 entries present checked in integrity check so (1) valid
13207  if(!AtLocSuccessor(AVEntry1))
13208  {
13209  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
13210  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
13211  TrainDataVector.clear();
13212  Utilities->CallLogPop(793);
13213  return(false);
13214  }
13215  }
13216  }
13217 
13218  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
13219  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13220  {
13221  bool FoundFlag = false;
13222  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13223  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
13224  // use reference so can change internals
13225  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
13226  {
13227  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
13228  {
13229  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
13230  if(AVEntry2.FormatType == TimeLoc)
13231  {
13232  FoundFlag = true;
13233  AVEntry.LocationName = AVEntry2.LocationName;
13234  break;
13235  }
13236  }
13237  if(!FoundFlag)
13238  {
13239  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
13240  TDEntry.HeadCode);
13241  TrainDataVector.clear();
13242  Utilities->CallLogPop(851);
13243  return(false);
13244  }
13245  }
13246  }
13247 
13248  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
13249  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13250  {
13251  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13252  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13253  {
13254  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13255  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
13256  {
13257  if(AVEntry.LocationName == "")
13258  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
13259  {
13260  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
13261  ": an event should have had a location name associated with it but it could not be found");
13262  TrainDataVector.clear();
13263  Utilities->CallLogPop(1831);
13264  return(false);
13265  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
13266  }
13267  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
13268  {
13269  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
13270  // use reference so can change internals where necessary
13271  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
13272  {
13273  AVEntry2.LocationName = AVEntry.LocationName;
13274  }
13275  else
13276  {
13277  break;
13278  }
13279  }
13280  }
13281  }
13282  }
13283  // all location names now set
13284 
13285  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
13286  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13287  {
13288  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13289  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13290  {
13291  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13292  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
13293  {
13294  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
13295  // i.e at least one more, must be a repeat
13296  {
13297  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
13298  {
13299  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
13300  TrainDataVector.clear();
13301  Utilities->CallLogPop(798);
13302  return(false);
13303  }
13304  }
13305  }
13306  if(AVEntry.Command == "F-nshs")
13307  {
13308  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
13309  // i.e has to be the last
13310  {
13311  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
13312  TrainDataVector.clear();
13313  Utilities->CallLogPop(1049);
13314  return(false);
13315  }
13316  }
13317  if(AVEntry.Command == "pas")
13318  {
13319  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13320  {
13321  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
13322  TrainDataVector.clear();
13323  Utilities->CallLogPop(1518);
13324  return(false);
13325  }
13326  }
13327  if(AVEntry.Command == "jbo")
13328  {
13329  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13330  {
13331  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
13332  TrainDataVector.clear();
13333  Utilities->CallLogPop(800);
13334  return(false);
13335  }
13336  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13337  if(!AtLocSuccessor(AVEntry2))
13338  {
13339  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
13340  ". The event isn't valid for a stationary train.");
13341  TrainDataVector.clear();
13342  Utilities->CallLogPop(801);
13343  return(false);
13344  }
13345  }
13346  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
13347  {
13348  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13349  {
13350  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
13351  TrainDataVector.clear();
13352  Utilities->CallLogPop(802);
13353  return(false);
13354  }
13355  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13356  if(!AtLocSuccessor(AVEntry2))
13357  {
13358  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
13359  ". The event isn't valid for a stationary train.");
13360  TrainDataVector.clear();
13361  Utilities->CallLogPop(803);
13362  return(false);
13363  }
13364  }
13365  if(AVEntry.Command == "cdt")
13366  {
13367  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13368  {
13369  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
13370  TrainDataVector.clear();
13371  Utilities->CallLogPop(804);
13372  return(false);
13373  }
13374  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13375  if(!AtLocSuccessor(AVEntry2))
13376  {
13377  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
13378  ". The event isn't valid for a stationary train.");
13379  TrainDataVector.clear();
13380  Utilities->CallLogPop(805);
13381  return(false);
13382  }
13383  }
13384  if(AVEntry.FormatType == TimeTimeLoc)
13385  {
13386  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13387  {
13388  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
13389  TrainDataVector.clear();
13390  Utilities->CallLogPop(806);
13391  return(false);
13392  }
13393  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13394  if(!MovingSuccessor(AVEntry2))
13395  {
13396  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
13397  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
13398  TrainDataVector.clear();
13399  Utilities->CallLogPop(807);
13400  return(false);
13401  }
13402  }
13403  if(AVEntry.FormatType == PassTime)
13404  {
13405  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13406  {
13407  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
13408  TrainDataVector.clear();
13409  Utilities->CallLogPop(1530);
13410  return(false);
13411  }
13412  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13413  if(!MovingSuccessor(AVEntry2))
13414  {
13415  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
13416  ". The event isn't valid for a moving train.");
13417  TrainDataVector.clear();
13418  Utilities->CallLogPop(1531);
13419  return(false);
13420  }
13421  }
13422  if(AVEntry.FormatType == Repeat)
13423  {
13424  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
13425  {
13426  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
13427  TrainDataVector.clear();
13428  Utilities->CallLogPop(808);
13429  return(false);
13430  }
13431  }
13432  }
13433  }
13434 
13435  // set arrival & departure times for TimeLocs & set their EventTimes to -1
13436  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13437  {
13438  bool LastEntryIsAnArrival = false;
13439  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
13440  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
13441  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13442  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
13443  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
13444  {
13445  LastEntryIsAnArrival = false;
13446  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13447  {
13448  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13449  if(AVEntry.FormatType == TimeLoc)
13450  {
13451  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
13452  {
13453  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
13454  }
13455  if(LastEntryIsAnArrival)
13456  {
13457  AVEntry.DepartureTime = AVEntry.EventTime;
13458  AVEntry.EventTime = TDateTime(-1);
13459  LastEntryIsAnArrival = false;
13460  }
13461  else // last entry a departure
13462  {
13463  AVEntry.ArrivalTime = AVEntry.EventTime;
13464  AVEntry.EventTime = TDateTime(-1);
13465  LastEntryIsAnArrival = true;
13466  }
13467  }
13468  }
13469  }
13470  else // all others stopped at beginning
13471  {
13472  LastEntryIsAnArrival = true;
13473  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13474  {
13475  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13476  if(AVEntry.FormatType == TimeLoc)
13477  {
13478  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
13479  {
13480  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
13481  }
13482  if(LastEntryIsAnArrival)
13483  {
13484  AVEntry.DepartureTime = AVEntry.EventTime;
13485  AVEntry.EventTime = TDateTime(-1);
13486  LastEntryIsAnArrival = false;
13487  }
13488  else // last entry a departure
13489  {
13490  AVEntry.ArrivalTime = AVEntry.EventTime;
13491  AVEntry.EventTime = TDateTime(-1);
13492  LastEntryIsAnArrival = true;
13493  }
13494  }
13495  }
13496  }
13497  }
13498  // perform remaining successor checks for TimeLocs
13499  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13500  {
13501  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13502  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13503  {
13504  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13505  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
13506  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
13507  {
13508  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13509  {
13510  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
13511  TrainDataVector.clear();
13512  Utilities->CallLogPop(809);
13513  return(false);
13514  }
13515  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13516  if(!AtLocSuccessor(AVEntry2))
13517  {
13518  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
13519  ". The event isn't valid for a stationary train.");
13520  TrainDataVector.clear();
13521  Utilities->CallLogPop(810);
13522  return(false);
13523  }
13524  }
13525  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
13526  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
13527  {
13528  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13529  {
13530  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
13531  TrainDataVector.clear();
13532  Utilities->CallLogPop(811);
13533  return(false);
13534  }
13535  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13536  if(!MovingSuccessor(AVEntry2))
13537  {
13538  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
13539  ". The event isn't valid for a moving train.");
13540  TrainDataVector.clear();
13541  Utilities->CallLogPop(812);
13542  return(false);
13543  }
13544  }
13545  }
13546  }
13547 
13548  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
13549  // & repeats have no times set
13550  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13551  {
13552  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13553  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13554  {
13555  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13556  if(AVEntry.FormatType == TimeLoc)
13557  {
13558  if(AVEntry.EventTime != TDateTime(-1))
13559  {
13560  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
13561  }
13562  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
13563  {
13564  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
13565  }
13566  }
13567  if(AVEntry.FormatType == TimeTimeLoc)
13568  {
13569  if(AVEntry.EventTime != TDateTime(-1))
13570  {
13571  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
13572  }
13573  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
13574  {
13575  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
13576  }
13577  }
13578  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
13579  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
13580  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
13581  {
13582  if(AVEntry.EventTime == TDateTime(-1))
13583  {
13584  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
13585  }
13586  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
13587  {
13588  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
13589  }
13590  }
13591  if(AVEntry.FormatType == Repeat)
13592  {
13593  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
13594  {
13595  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
13596  }
13597  }
13598  }
13599  }
13600 
13601  // check times stay same or increase, note that can have time of 0 if include midnight
13602  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13603  {
13604  TDateTime CurrentTime = TTClockTime; // the timetable start time
13605  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13606  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13607  {
13608  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13609  if(AVEntry.FormatType == Repeat)
13610  {
13611  break;
13612  }
13613  if(AVEntry.FormatType == FinRemHere)
13614  {
13615  break;
13616  }
13617  if(AVEntry.FormatType == TimeTimeLoc)
13618  {
13619  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
13620  {
13621  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
13622  TDEntry.HeadCode);
13623  TrainDataVector.clear();
13624  Utilities->CallLogPop(813);
13625  return(false);
13626  }
13627  if(AVEntry.ArrivalTime < CurrentTime)
13628  {
13629  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
13630  TDEntry.HeadCode);
13631  TrainDataVector.clear();
13632  Utilities->CallLogPop(814);
13633  return(false);
13634  }
13635  CurrentTime = AVEntry.DepartureTime;
13636  continue;
13637  }
13638  if(AVEntry.FormatType == TimeLoc)
13639  {
13640  if(AVEntry.ArrivalTime >= TDateTime(0))
13641  {
13642  if(AVEntry.ArrivalTime < CurrentTime)
13643  {
13644  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
13645  TrainDataVector.clear();
13646  Utilities->CallLogPop(815);
13647  return(false);
13648  }
13649  CurrentTime = AVEntry.ArrivalTime;
13650  }
13651  else
13652  {
13653  if(AVEntry.DepartureTime < CurrentTime)
13654  // both may be 0 legitimately so must allow for this
13655  {
13656  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
13657  TrainDataVector.clear();
13658  Utilities->CallLogPop(816);
13659  return(false);
13660  }
13661  CurrentTime = AVEntry.DepartureTime;
13662  }
13663  continue;
13664  }
13665  if(AVEntry.EventTime < CurrentTime)
13666  // all others have EventTime set
13667  {
13668  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
13669  ", may be before timetable start time");
13670  TrainDataVector.clear();
13671  Utilities->CallLogPop(835);
13672  return(false);
13673  }
13674  CurrentTime = AVEntry.EventTime;
13675  continue;
13676  }
13677  }
13678 
13679  // check locations consistent
13680  AnsiString LastLocationName = "";
13681 
13682  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13683  {
13684  bool LastEntryIsAnArrival = false;
13685  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13686  // first deal with moving Snt entries (all else stopped)
13687  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
13688  {
13689  LastEntryIsAnArrival = false;
13690  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
13691  if(LastLocationName != "")
13692  {
13693  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
13694  }
13695  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
13696  y++) // note that immediate successor to a moving Snt can only be a Moving type
13697  {
13698  // if it's a SignallerControl entry then the condition isn't met
13699  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13700  if(AVEntry.FormatType == Repeat)
13701  {
13702  break; // repeat = reached end (+allows repeat after signaller controlled entry)
13703  }
13704  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
13705  {
13706  if(AVEntry.LocationName != LastLocationName)
13707  {
13708  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13709  AVEntry.Command);
13710  TrainDataVector.clear();
13711  Utilities->CallLogPop(823);
13712  return(false);
13713  }
13714  }
13715  else if(AVEntry.FormatType == TimeCmd)
13716  // cdt is the only TimeCmd
13717  {
13718  if(AVEntry.LocationName != LastLocationName)
13719  {
13720  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13721  AVEntry.Command);
13722  TrainDataVector.clear();
13723  Utilities->CallLogPop(824);
13724  return(false);
13725  }
13726  }
13727  else if(AVEntry.FormatType == TimeTimeLoc)
13728  {
13729  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13730  // last entry must be a departure or would have failed earlier
13731  {
13732  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13733  TwoLocationFlag = true;
13734 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13735 // TwoOrMoreLocationsWarningGiven = true;
13736  }
13737  LastLocationName = AVEntry.LocationName;
13738  LastEntryIsAnArrival = false;
13739  }
13740  else if(AVEntry.FormatType == TimeLoc)
13741  {
13742  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
13743  {
13744  SecondPassMessage(GiveMessages,
13745  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
13746  TrainDataVector.clear();
13747  Utilities->CallLogPop(826);
13748  return(false);
13749  }
13750  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
13751  {
13752  SecondPassMessage(GiveMessages,
13753  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
13754  TrainDataVector.clear();
13755  Utilities->CallLogPop(827);
13756  return(false);
13757  }
13758  LastLocationName = AVEntry.LocationName;
13759  LastEntryIsAnArrival = !LastEntryIsAnArrival;
13760  }
13761  }
13762  }
13763  else // all stationary starting entries
13764  {
13765  LastEntryIsAnArrival = true;
13766  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
13767  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13768  {
13769  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13770  if(AVEntry.FormatType == Repeat)
13771  {
13772  break;
13773  }
13774  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
13775  // no need to add anything for shuttle starts since they are at loc (0) anyway
13776  {
13777  if(AVEntry.LocationName != LastLocationName)
13778  {
13779  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13780  AVEntry.Command);
13781  TrainDataVector.clear();
13782  Utilities->CallLogPop(828);
13783  return(false);
13784  }
13785  }
13786  else if(AVEntry.FormatType == TimeCmd)
13787  // cdt is the only TimeCmd
13788  {
13789  if(AVEntry.LocationName != LastLocationName)
13790  {
13791  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13792  AVEntry.Command);
13793  TrainDataVector.clear();
13794  Utilities->CallLogPop(829);
13795  return(false);
13796  }
13797  }
13798  else if(AVEntry.FormatType == TimeTimeLoc)
13799  {
13800  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13801  // last entry must be a departure or would have failed earlier
13802  {
13803  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13804  TwoLocationFlag = true;
13805 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13806 // TwoOrMoreLocationsWarningGiven = true;
13807  }
13808  LastLocationName = AVEntry.LocationName;
13809  LastEntryIsAnArrival = false;
13810  }
13811  else if(AVEntry.FormatType == TimeLoc)
13812  {
13813  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
13814  {
13815  SecondPassMessage(GiveMessages,
13816  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
13817  TrainDataVector.clear();
13818  Utilities->CallLogPop(831);
13819  return(false);
13820  }
13821  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
13822  {
13823  SecondPassMessage(GiveMessages,
13824  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
13826 // TrainDataVector.clear();
13827 // Utilities->CallLogPop(832);
13828 // return false;
13829  }
13830  LastLocationName = AVEntry.LocationName;
13831  LastEntryIsAnArrival = !LastEntryIsAnArrival;
13832  }
13833  }
13834  }
13835  }
13836 
13837  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
13838  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
13839  AnsiString LocationNameToBeChecked = "";
13840 
13841  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13842  {
13843  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
13844  unsigned int y = 0;
13845  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
13846  // first discard unlocated Snt entries as they don't have location name set
13847  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
13848  {
13849  y = 1;
13850  }
13851  while(y < TDEntry.ActionVector.size())
13852  // need to check each location name separately in turn, skipped for SignallerControl entries
13853  {
13854  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
13855  {
13856  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
13857  }
13858  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
13859  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
13860  {
13861  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
13862  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
13863  {
13864  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
13865  }
13866  if(AVEntry.Command == "cdt")
13867  {
13868  break; // out of the 'z' loop since the check is only valid up to a change of direction
13869  }
13870  if(AVEntry.LocationName == LocationNameToBeChecked)
13871  {
13872  continue; // keep going while name same
13873  }
13874  if(AVEntry.LocationName != LocationNameToBeChecked)
13875  // if name different check forwards to see if repeats
13876  {
13877  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
13878  {
13879  if(TDEntry.ActionVector.at(a).Command == "cdt")
13880  {
13881  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
13882  }
13883  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13884  {
13885  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13886  TwoLocationFlag = true;
13887 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13888 // TwoOrMoreLocationsWarningGiven = true;
13889  }
13890  }
13891  break; // out of the 'z' loop since have checked 'a' as far as need to
13892  }
13893  }
13894  y++;
13895  }
13896  }
13897  if(TwoLocationFlag)
13898  {
13899  TwoLocationList.sort(); //need to sort first in alphabetical order to ensure all duplictes removed
13900  TwoLocationList.unique(); //remove duplicates
13901  }
13902 
13903  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
13904  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13905  {
13906  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13907  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13908  {
13909  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13910  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
13911  {
13912  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
13913  }
13914  AnsiString LocName = "";
13915  // dummy, only used so can call IsSNTEntryLocated
13916  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
13917  {
13918  if(AVEntry.LocationName == "")
13919  {
13920  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
13921  }
13922  }
13923  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
13924  {
13925  if(AVEntry.LocationName != "")
13926  {
13927  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
13928  }
13929  }
13930  }
13931  }
13932 
13933 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
13934  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
13935  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
13936 
13937  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
13938  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
13939 
13940  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
13941  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
13942  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
13943 */
13944  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
13945  {
13946  // non-shuttles & non-repeating links separately, but don't check that there isn't a
13947  // duplicate between a non-repeating shuttle and another - leave original tests in as
13948  // these also set the pointers
13949  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13950  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13951  {
13952  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13953  if(AVEntry.OtherHeadCode != "")
13954  {
13955  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
13956  {
13957  Utilities->CallLogPop(1584);
13958  return(false); // error message given in called function
13959  }
13960  }
13961  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13962  {
13963  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
13964  {
13965  Utilities->CallLogPop(1585);
13966  return(false); // error message given in called function
13967  }
13968  }
13969  }
13970  }
13971 
13972  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13973  {
13974  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13975  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13976  {
13977  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13978  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13979  {
13980  if(AVEntry.OtherHeadCode != "")
13981  {
13982  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
13983  // false = non-shuttle
13984  {
13985  Utilities->CallLogPop(864);
13986  return(false); // error message given in called function
13987  }
13988  }
13989  }
13990  }
13991  }
13992 
13993  // now repeat the check just for the shuttles
13994  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13995  {
13996  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13997  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13998  {
13999  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14000  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
14001  {
14002  if(AVEntry.OtherHeadCode != "")
14003  {
14004  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
14005  // true = shuttle
14006  {
14007  Utilities->CallLogPop(1100);
14008  return(false); // error message given in called function
14009  }
14010  }
14011  }
14012  }
14013  }
14014 
14015  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14016  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14017  {
14018  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14019  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14020  {
14021  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14022  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
14023  {
14025  {
14026  Utilities->CallLogPop(1060);
14027  return(false); // error message given in called function
14028  }
14029  }
14030  }
14031  }
14032 
14033  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14034  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14035  // don't ever need to and as designed would skip repeats
14036  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14037  {
14038  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14039  {
14040  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14041  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
14042  {
14043  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
14044  {
14045  Utilities->CallLogPop(1090);
14046  return(false); // error message given in called function
14047  }
14048  }
14049  }
14050  }
14051 
14052  // check all entries have all types set to something
14053  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14054  {
14055  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14056  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14057  {
14058  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14059  if(AVEntry.FormatType == NoFormat)
14060  {
14061  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
14062  }
14063  else if(AVEntry.SequenceType == NoSequence)
14064  {
14065  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
14066  }
14067  else if(AVEntry.LocationType == NoLocation)
14068  {
14069  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
14070  }
14071  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
14072  {
14073  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
14074  }
14075  }
14076  }
14077 
14078  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
14079  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14080  {
14081  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14082  // non-const reference so can alter content
14083  TTrainOperatingData TData;
14084  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
14085  if(LastAVEntry.FormatType == Repeat) // check if a repeat
14086  {
14087 /*
14088  class TTrainOperatingData
14089  {
14090  public:
14091  int TrainID; - default, set at construction
14092  TActionEventType EventReported; used during operation
14093  TRunningEntry RunningEntry; - default, set at construction
14094  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
14095  };
14096 */
14097  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
14098  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
14099  {
14100  TDEntry.TrainOperatingDataVector.push_back(TData);
14101  }
14102  }
14103  else
14104  {
14105  TDEntry.NumberOfTrains = 1;
14106  }
14107  }
14108 
14109  // check that don't include any Continuation names
14110  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14111  {
14112  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14113  {
14114  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
14115  AnsiString HC = TrainDataVector.at(x).HeadCode;
14116  if(LocName != "")
14117  {
14118  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
14119  {
14120  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
14121  TrainDataVector.clear();
14122  Utilities->CallLogPop(1578);
14123  return(false);
14124  }
14125  }
14126  }
14127  }
14128 
14129  // check that all repeat times below 96h
14130  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14131  {
14132  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
14133  int IncMinutes = 0;
14134  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
14135  {
14136  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
14137  }
14138  else
14139  {
14140  continue; // basic times already checked in CheckTimeValidity
14141  }
14142  AnsiString HC = TrainDataVector.at(x).HeadCode;
14143  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14144  {
14145  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
14146  {
14147  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14148  {
14149  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
14150  TrainDataVector.clear();
14151  Utilities->CallLogPop(1818);
14152  return(false);
14153  }
14154  }
14155  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
14156  {
14157  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14158  // 3d 23h 59m = 3.9993055556
14159  {
14160  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
14161  TrainDataVector.clear();
14162  Utilities->CallLogPop(1819);
14163  return(false);
14164  }
14165  }
14166  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
14167  {
14168  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14169  // 3d 23h 59m = 3.9993055556
14170  {
14171  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
14172  TrainDataVector.clear();
14173  Utilities->CallLogPop(1820);
14174  return(false);
14175  }
14176  }
14177  }
14178  }
14179 
14180  // Now that all set up change any extended headcodes back to ordinary headcodes
14181  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14182  {
14183  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
14184  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14185  {
14186  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
14187  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
14188  }
14189  }
14190 
14191  // SaveTrainDataVectorToFile(0);//for testing purposes
14193  Utilities->CallLogPop(782);
14194  return(true);
14195 }
14196 
14197 // ---------------------------------------------------------------------------
14198 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
14200 {
14201  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
14202 }
14203 
14204 // ---------------------------------------------------------------------------
14205 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
14207 {
14208  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
14209  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
14210  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
14211 }
14212 
14213 // ---------------------------------------------------------------------------
14214 
14215 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
14216 {
14217  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
14218  if(HeadCode.Length() > 4) // ignore otherwise
14219  {
14220  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
14221  }
14222  Utilities->CallLogPop(1593);
14223 }
14224 
14225 // ---------------------------------------------------------------------------
14226 
14227 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
14228 {
14229  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
14230  SecondHeadCode);
14231  int ForwardCount = 0;
14232  int ReverseCount = 0;
14233 
14234  if(MainHeadCode == SecondHeadCode)
14235  {
14236  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
14237  TrainDataVector.clear();
14238  Utilities->CallLogPop(1594);
14239  return(false);
14240  }
14241  // forward check
14242  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14243  {
14244  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14245  if(TDEntry.HeadCode == MainHeadCode)
14246  {
14247  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14248  {
14249  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14250  if(AVEntry.OtherHeadCode == SecondHeadCode)
14251  {
14252  ForwardCount++;
14253  }
14254  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
14255  // need own check in case both 'Other' & 'NonRepeating' have same headcode
14256  {
14257  ForwardCount++;
14258  }
14259  }
14260  }
14261  }
14262  if(ForwardCount == 0)
14263  // this is an exception because the headcodes are selected in the same order as the forward check
14264  {
14265  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
14266  }
14267  if(ForwardCount > 2)
14268  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
14269  {
14270  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
14271  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
14272  TrainDataVector.clear();
14273  Utilities->CallLogPop(1587);
14274  return(false);
14275  }
14276  // reverse check
14277  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14278  {
14279  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14280  if(TDEntry.HeadCode == SecondHeadCode)
14281  {
14282  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14283  {
14284  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14285  if(AVEntry.OtherHeadCode == MainHeadCode)
14286  {
14287  ReverseCount++;
14288  }
14289  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14290  {
14291  ReverseCount++;
14292  }
14293  }
14294  }
14295  }
14296 
14297  if(ReverseCount == 0)
14298  {
14299  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
14300  TrainDataVector.clear();
14301  Utilities->CallLogPop(1588);
14302  return(false);
14303  }
14304  if(ReverseCount > 2)
14305  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
14306  {
14307  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
14308  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
14309  TrainDataVector.clear();
14310  Utilities->CallLogPop(1589);
14311  return(false);
14312  }
14313  if(ForwardCount != ReverseCount)
14314  {
14315  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
14316  " than the other way round");
14317  TrainDataVector.clear();
14318  Utilities->CallLogPop(1610);
14319  return(false);
14320  }
14321  Utilities->CallLogPop(1590);
14322  return(true);
14323 }
14324 
14325 // ---------------------------------------------------------------------------
14326 
14327 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
14328 /* Return false for no find or more than one find, check correct types of link
14329  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
14330  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
14331  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
14332  Then do the same in reverse.
14333  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
14334  if main is Fns other must be Sns; if main is jbo other must be Fjo.
14335  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
14336  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
14337  for Sfs & Sns services. Finally check the repeat entries if present are consistent
14338 
14339  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
14340  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
14341 
14342  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
14343 */
14344 
14345 {
14346  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
14347  int ForwardCount = 0;
14348  int ReverseCount = 0;
14349  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
14350  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
14351  TTrainDataEntry *MainTrainDataPtr = 0;
14352  TTrainDataEntry *OtherTrainDataPtr = 0;
14353 
14354  // forward check
14355  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14356  {
14357  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14358  if(TDEntry.HeadCode == MainHeadCode)
14359  {
14360  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14361  {
14362  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14363  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
14364  {
14365  if(AVEntry.OtherHeadCode == OtherHeadCode)
14366  {
14367  MainTrainDataPtr = &TrainDataVector.at(x);
14368  ForwardEntryPtr = &AVEntry;
14369  ForwardCount++;
14370  ForwardTDVectorNumber = x;
14371  }
14372  }
14373  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
14374  (AVEntry.Command == "Frh-sh")))
14375  {
14376  if(AVEntry.OtherHeadCode == OtherHeadCode)
14377  {
14378  MainTrainDataPtr = &TrainDataVector.at(x);
14379  ForwardEntryPtr = &AVEntry;
14380  ForwardCount++;
14381  ForwardTDVectorNumber = x;
14382  }
14383  }
14384  }
14385  }
14386  }
14387  if(ForwardCount == 0)
14388  // this is an exception because the headcodes are selected in the same order as the forward check
14389  {
14390  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
14391  }
14392  if(ForwardCount > 1)
14393  {
14394  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
14395  MainHeadCode);
14396  TrainDataVector.clear();
14397  Utilities->CallLogPop(836);
14398  return(false);
14399  }
14400  // reverse check
14401  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14402  {
14403  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14404  if(TDEntry.HeadCode == OtherHeadCode)
14405  {
14406  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14407  {
14408  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14409  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
14410  {
14411  if(AVEntry.OtherHeadCode == MainHeadCode)
14412  {
14413  OtherTrainDataPtr = &TrainDataVector.at(x);
14414  ReverseCount++;
14415  ReverseEntryPtr = &AVEntry;
14416  ReverseTDVectorNumber = x;
14417  }
14418  }
14419  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
14420  {
14421  if(AVEntry.OtherHeadCode == MainHeadCode)
14422  {
14423  OtherTrainDataPtr = &TrainDataVector.at(x);
14424  ReverseCount++;
14425  ReverseEntryPtr = &AVEntry;
14426  ReverseTDVectorNumber = x;
14427  }
14428  }
14429  }
14430  }
14431  }
14432 
14433  if(ReverseCount == 0)
14434  {
14435  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
14436  TrainDataVector.clear();
14437  Utilities->CallLogPop(837);
14438  return(false);
14439  }
14440  if(ReverseCount > 1)
14441  {
14442  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
14443  OtherHeadCode);
14444  TrainDataVector.clear();
14445  Utilities->CallLogPop(838);
14446  return(false);
14447  }
14448  // these will all be false for !Shuttle
14449  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
14450  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
14451  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
14452  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
14453 
14454  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
14455  {
14456  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
14457  TrainDataVector.clear();
14458  Utilities->CallLogPop(1058);
14459  return(false);
14460  }
14461  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
14462  {
14463  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
14464  TrainDataVector.clear();
14465  Utilities->CallLogPop(1059);
14466  return(false);
14467  }
14468  if(ForwardEntryPtr->LocationName == "")
14469  {
14470  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
14471  ". One or other service does not have a location set");
14472  TrainDataVector.clear();
14473  Utilities->CallLogPop(526);
14474  return(false);
14475  }
14476  if(ReverseEntryPtr->LocationName == "")
14477  {
14478  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
14479  ". One or other service does not have a location set");
14480  TrainDataVector.clear();
14481  Utilities->CallLogPop(527);
14482  return(false);
14483  }
14484  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
14485  {
14486  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
14487  " is at a different location to the referencing train " + MainHeadCode);
14488  TrainDataVector.clear();
14489  Utilities->CallLogPop(842);
14490  return(false);
14491  }
14492  // ignore shuttle repeat links for first time check
14493  if(!Shuttle)
14494  {
14495  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
14496  {
14497  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
14498  " has a different event time to the referencing train " + MainHeadCode);
14499  TrainDataVector.clear();
14500  Utilities->CallLogPop(525);
14501  return(false);
14502  }
14503  }
14504  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
14505  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
14506  if(ForwardShuttleStart && ReverseShuttleFinish)
14507  // Shuttle must be true if these are true
14508  {
14509  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
14510  {
14511  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
14512  " first repeat restart time not consistent with finish service " + OtherHeadCode);
14513  TrainDataVector.clear();
14514  Utilities->CallLogPop(1055);
14515  return(false);
14516  }
14517  }
14518  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
14519  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
14520  {
14521  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14522  {
14523  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
14524  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
14525  TrainDataVector.clear();
14526  Utilities->CallLogPop(528);
14527  return(false);
14528  }
14529  }
14530  if(ReverseEntryPtr->Command == "Fjo")
14531  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
14532  {
14533  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14534  {
14535  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
14536  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
14537  TrainDataVector.clear();
14538  Utilities->CallLogPop(862);
14539  return(false);
14540  }
14541  }
14542  if(ReverseEntryPtr->Command == "Fns")
14543  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
14544  {
14545  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14546  {
14547  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
14548  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
14549  TrainDataVector.clear();
14550  Utilities->CallLogPop(529);
14551  return(false);
14552  }
14553  }
14554  if(ForwardEntryPtr->Command == "Sfs")
14555  {
14556  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
14557  {
14558  SecondPassMessage(GiveMessages,
14559  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
14560  MainHeadCode);
14561  TrainDataVector.clear();
14562  Utilities->CallLogPop(530);
14563  return(false);
14564  }
14565  }
14566  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
14567  {
14568  if(ReverseEntryPtr->Command != "Sfs")
14569  {
14570  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
14571  MainHeadCode);
14572  TrainDataVector.clear();
14573  Utilities->CallLogPop(839);
14574  return(false);
14575  }
14576  else
14577  {
14578  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
14579  {
14580  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
14581  TrainDataVector.clear();
14582  Utilities->CallLogPop(849);
14583  return(false);
14584  }
14585  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
14586  {
14587  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
14588  TrainDataVector.clear();
14589  Utilities->CallLogPop(850);
14590  return(false);
14591  }
14592  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
14593  {
14594  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
14595  TrainDataVector.clear();
14596  Utilities->CallLogPop(846);
14597  return(false);
14598  }
14599  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14600  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14601  if(OtherTrainDataPtr->Description == "")
14602  {
14603  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14604  }
14605  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
14606  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14607  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14608  }
14609  }
14610  if(ForwardEntryPtr->Command == "Sns")
14611  {
14612  if(ReverseEntryPtr->Command != "Fns")
14613  {
14614  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
14615  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
14616  TrainDataVector.clear();
14617  Utilities->CallLogPop(531);
14618  return(false);
14619  }
14620  }
14621  if(ForwardEntryPtr->Command == "Fns")
14622  {
14623  if(ReverseEntryPtr->Command != "Sns")
14624  {
14625  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
14626  " and forms a new service with headcode " + OtherHeadCode);
14627  TrainDataVector.clear();
14628  Utilities->CallLogPop(840);
14629  return(false);
14630  }
14631  else
14632  {
14633  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14634  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14635  if(OtherTrainDataPtr->Description == "")
14636  {
14637  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14638  }
14639  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14640  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14641  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14642  }
14643  }
14644  if(ForwardEntryPtr->Command == "jbo")
14645  {
14646  if(ReverseEntryPtr->Command != "Fjo")
14647  {
14648  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
14649  " and is joined by a train with headcode " + OtherHeadCode);
14650  TrainDataVector.clear();
14651  Utilities->CallLogPop(841);
14652  return(false);
14653  }
14654  }
14655  if(ForwardEntryPtr->Command == "Fjo")
14656  {
14657  if(ReverseEntryPtr->Command != "jbo")
14658  {
14659  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
14660  " and joins a train with headcode " + OtherHeadCode);
14661  TrainDataVector.clear();
14662  Utilities->CallLogPop(532);
14663  return(false);
14664  }
14665  else
14666  {
14667  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14668  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14669  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
14670  {
14671  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14672  }
14673  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
14674  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
14675  }
14676  }
14677  if(ForwardShuttleStart)
14678  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
14679  {
14680  if(!ReverseShuttleFinish)
14681  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
14682  {
14683  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
14684  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
14685  TrainDataVector.clear();
14686  Utilities->CallLogPop(1056);
14687  return(false);
14688  }
14689  }
14690  if(ReverseShuttleStart)
14691  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
14692  {
14693  if(!ForwardShuttleFinish)
14694  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
14695  {
14696  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
14697  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
14698  TrainDataVector.clear();
14699  Utilities->CallLogPop(1057);
14700  return(false);
14701  }
14702  else
14703  {
14704  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14705  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14706 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
14707  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
14708 */
14709  }
14710  }
14711  // check repeat information consistent if present
14712  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
14713  // and those not accessed here
14714 
14715  // still need to check the non-repeating links and that they have no repeats - do that outside this function
14716  bool MainRepeat = false, OtherRepeat = false;
14717  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
14718 
14719  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
14720  {
14721  MainRepeat = true;
14722  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
14723  }
14724  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
14725  {
14726  OtherRepeat = true;
14727  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
14728  }
14729  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
14730  {
14731  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
14732  " and the associated train with headcode " + OtherHeadCode);
14733  TrainDataVector.clear();
14734  Utilities->CallLogPop(844);
14735  return(false);
14736  }
14737  if(MainRepeat && OtherRepeat)
14738  {
14739  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
14740  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
14741  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
14742  {
14743  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
14744  " and the associated train with headcode " + OtherHeadCode);
14745  TrainDataVector.clear();
14746  Utilities->CallLogPop(845);
14747  return(false);
14748  }
14749  }
14750  Utilities->CallLogPop(863);
14751  return(true);
14752 }
14753 
14754 // ---------------------------------------------------------------------------
14755 
14756 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
14757 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
14758 {
14759  // strip spaces from extreme ends of input
14760  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
14761  if(Input == "")
14762  {
14763  Utilities->CallLogPop(856);
14764  return;
14765  }
14766  while(Input[1] == ' ')
14767  {
14768  if(Input.Length() > 1)
14769  {
14770  Input = Input.SubString(2, Input.Length() - 1);
14771  }
14772  else
14773  {
14774  Input = "";
14775  Utilities->CallLogPop(857);
14776  return;
14777  }
14778  }
14779  if(Input == "")
14780  {
14781  Utilities->CallLogPop(858);
14782  return;
14783  }
14784  while(Input[Input.Length()] == ' ')
14785  {
14786  if(Input.Length() > 1)
14787  {
14788  Input = Input.SubString(1, Input.Length() - 1);
14789  }
14790  else
14791  {
14792  Input = "";
14793  Utilities->CallLogPop(859);
14794  return;
14795  }
14796  }
14797  // now strip spaces immediately after all commas and semicolons within the text
14798  AnsiString Output = "";
14799  bool DelimiterFound = false;
14800 
14801  for(int x = 1; x < Input.Length() + 1; x++)
14802  {
14803  if(DelimiterFound)
14804  {
14805  if(Input[x] == ' ')
14806  {
14807  continue;
14808  }
14809  }
14810  if((Input[x] != ',') && (Input[x] != ';'))
14811  {
14812  DelimiterFound = false;
14813  Output = Output + Input[x];
14814  }
14815  else
14816  {
14817  DelimiterFound = true;
14818  Output = Output + Input[x];
14819  }
14820  }
14821  if(Output == "")
14822  {
14823  Input = "";
14824  Utilities->CallLogPop(860);
14825  return;
14826  }
14827  // now strip spaces immediately before all commas and semicolons within the text
14828  Input = Output;
14829  Output = "";
14830  DelimiterFound = false;
14831  for(int x = Input.Length(); x > 0; x--)
14832  {
14833  if(DelimiterFound)
14834  {
14835  if(Input[x] == ' ')
14836  {
14837  continue;
14838  }
14839  }
14840  if((Input[x] != ',') && (Input[x] != ';'))
14841  {
14842  DelimiterFound = false;
14843  Output = AnsiString(Input[x]) + Output;
14844  }
14845  else
14846  {
14847  DelimiterFound = true;
14848  Output = AnsiString(Input[x]) + Output;
14849  }
14850  }
14851  Input = Output;
14852  Utilities->CallLogPop(861);
14853 }
14854 
14855 // ---------------------------------------------------------------------------
14856 
14857 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
14858 // checks if an Snt or Snt-sh entry with zero starting speed is followed (somewhere, not necessarily immediately) by a TimeLoc & has the same LocationName
14859 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
14860 // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
14861 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
14862 // are done in this function, they must be done elsewhere.
14863 //a starting speed > 0 always returns false
14864 {
14865  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
14866  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
14867  LocationName = "";
14868  if(TDEntry.StartSpeed > 0)
14869  {
14870  Utilities->CallLogPop(1784);
14871  return(false);
14872  }
14873  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
14874  {
14875  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
14876  }
14878  {
14879  Utilities->CallLogPop(852);
14880  return(false);
14881  }
14882  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
14883  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
14884 
14885  if(LocRear != "")
14886  {
14887  LocationName = LocRear;
14888  }
14889  else
14890  {
14891  LocationName = LocFront;
14892  }
14893  if(LocationName == "")
14894  {
14895  Utilities->CallLogPop(1036);
14896  return(false);
14897  }
14898  if(AVEntry0.SignallerControl)
14899  {
14900  Utilities->CallLogPop(1773);
14901  return(true);
14902  }
14903 // here if not a signaller start entry so must be at least one more entry, and it is at a location
14904 
14905 //Ok Not ok continue
14906 
14907 //Frh if Snt Frh-sh cdt
14908 //Fns if Snt Fns-sh fsp or rsp
14909 //Fjo if Snt TimeTimeLoc jbo
14910 //F-nshs if Snt pas
14911 //TimeLoc dep Fer
14912 // TimeLoc arr
14913 
14914  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
14915  {
14916  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
14917  if(((AVEntry.Command == "Frh") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "F-nshs") || (AVEntry.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
14918  {
14919  Utilities->CallLogPop(1037);
14920  return(true);
14921  }
14922  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName == LocationName)) //will be a departure if same name- times not set yet so can't use them to confirm
14923  {
14924  Utilities->CallLogPop(2442);
14925  return(true);
14926  }
14927  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName != LocationName)) //arrival, not located
14928  {
14929  Utilities->CallLogPop(2438);
14930  return(false);
14931  }
14932  if((AVEntry.Command == "Fer") || (AVEntry.Command == "pas") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh") || (AVEntry.FormatType == TimeTimeLoc))
14933  {
14934  Utilities->CallLogPop(854);
14935  return(false);
14936  }
14937  if((AVEntry.Command == "cdt") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "jbo"))
14938  {
14939  continue;
14940  }
14941  }
14942  Utilities->CallLogPop(855);
14943  return(false);
14944 
14945 }
14946 
14947 // ---------------------------------------------------------------------------
14948 
14949 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
14950 {
14951  // checks that the new train start elements are valid - both exist & are connected, and that not
14952  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
14953  // & not starting with front on a continuation
14954  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
14955  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
14956 
14957  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
14958  if(RearPosition < 0)
14959  // error message given in GetTrackVectorPositionFromString
14960  {
14961  Utilities->CallLogPop(759);
14962  return(false);
14963  }
14964  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
14965  if(FrontPosition < 0)
14966  // error message given in GetTrackVectorPositionFromString
14967  {
14968  Utilities->CallLogPop(760);
14969  return(false);
14970  }
14971  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
14972  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
14973  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
14974 
14975  // check front & rear connected
14976  for(int x = 0; x < 4; x++)
14977  {
14978  if(RearTrackElement.Conn[x] == FrontPosition)
14979  {
14980  RearExitPos = x;
14981  break;
14982  }
14983  if(x == 3)
14984  {
14985  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
14986  Utilities->CallLogPop(762);
14987  return(false);
14988  }
14989  }
14990  // check not starting with front on a continuation
14991  if(FrontType == Continuation)
14992  {
14993  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
14994  Utilities->CallLogPop(937);
14995  return(false);
14996  }
14997  // check not starting on a level crossing
14998  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
14999  {
15000  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
15001  Utilities->CallLogPop(1951);
15002  return(false);
15003  }
15004  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
15005  {
15006  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
15007  Utilities->CallLogPop(1952);
15008  return(false);
15009  }
15010  // check if trying to start on diverging leg of points
15011  if((RearType == Points) && (RearExitPos == 3))
15012  {
15013  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
15014  Utilities->CallLogPop(936);
15015  return(false);
15016  }
15017  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
15018  {
15019  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
15020  Utilities->CallLogPop(1808);
15021  return(false);
15022  }
15023  Utilities->CallLogPop(905);
15024  return(true);
15025 }
15026 
15027 // ---------------------------------------------------------------------------
15028 
15029 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
15030 // Rear & front element validity already checked in CheckStartPositionValidity
15031 // This checks for points in correct orientation, no train at start position and not starting on a locked route
15032 {
15033  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
15034  AnsiString(RearExitPos));
15035  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
15036 
15037  if(RearTrackElement.TrackType == Continuation)
15038  {
15039  EventType = FailTrainEntry;
15040  }
15041  else
15042  {
15043  EventType = FailCreateTrain;
15044  }
15045  int FrontPosition = RearTrackElement.Conn[RearExitPos];
15046  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
15047  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
15048  TTrackType RearType = RearTrackElement.TrackType;
15049  TTrackType FrontType = FrontTrackElement.TrackType;
15050  AnsiString RearName, FrontName;
15051 
15052  if(RearTrackElement.ActiveTrackElementName != "")
15053  {
15054  RearName = RearTrackElement.ActiveTrackElementName;
15055  }
15056  else
15057  {
15058  RearName = RearTrackElement.ElementID;
15059  }
15060  if(FrontTrackElement.ActiveTrackElementName != "")
15061  {
15062  FrontName = FrontTrackElement.ActiveTrackElementName;
15063  }
15064  else
15065  {
15066  FrontName = FrontTrackElement.ElementID;
15067  }
15068  TPrefDirElement PrefDirElement; // needed for next function but not used
15069  int LockedVectorNumber; // needed for next function but not used
15070 
15071  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
15072  {
15073  if(ReportFlag)
15074  {
15075  if(EventType == FailCreateTrain)
15076  {
15077  EventType = FailCreateLockedRoute;
15078  }
15079  else
15080  {
15081  EventType = FailEnterLockedRoute;
15082  }
15083  LogActionError(47, HeadCode, "", EventType, FrontName);
15084  }
15085  Utilities->CallLogPop(940);
15086  return(false);
15087  }
15088  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
15089  {
15090  if(ReportFlag)
15091  {
15092  if(EventType == FailCreateTrain)
15093  {
15094  EventType = FailCreateLockedRoute;
15095  }
15096  else
15097  {
15098  EventType = FailEnterLockedRoute;
15099  }
15100  LogActionError(48, HeadCode, "", EventType, RearName);
15101  }
15102  Utilities->CallLogPop(1809);
15103  return(false);
15104  }
15105  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
15106  {
15107  if(ReportFlag)
15108  {
15109  LogActionError(27, HeadCode, "", EventType, RearName);
15110  }
15111  Utilities->CallLogPop(1810);
15112  return(false);
15113  }
15114  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
15115  {
15116  if(ReportFlag)
15117  {
15118  if(EventType == FailCreateTrain)
15119  {
15120  LogActionError(28, HeadCode, "", EventType, FrontName);
15121  }
15122  else
15123  {
15124  LogActionError(43, HeadCode, "", EventType, RearName);
15125  }
15126  }
15127  Utilities->CallLogPop(941);
15128  return(false);
15129  }
15130  if(RearType == Bridge)
15131  {
15132  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
15133  {
15134  if(ReportFlag)
15135  {
15136  LogActionError(29, HeadCode, "", EventType, RearName);
15137  }
15138  Utilities->CallLogPop(942);
15139  return(false);
15140  }
15141  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
15142  {
15143  if(ReportFlag)
15144  {
15145  LogActionError(30, HeadCode, "", EventType, RearName);
15146  }
15147  Utilities->CallLogPop(943);
15148  return(false);
15149  }
15150  }
15151  if(FrontType == Bridge)
15152  {
15153  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
15154  {
15155  if(ReportFlag)
15156  {
15157  if(EventType == FailCreateTrain)
15158  {
15159  LogActionError(31, HeadCode, "", EventType, FrontName);
15160  }
15161  else
15162  {
15163  LogActionError(44, HeadCode, "", EventType, RearName);
15164  }
15165  }
15166  Utilities->CallLogPop(944);
15167  return(false);
15168  }
15169  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
15170  {
15171  if(ReportFlag)
15172  {
15173  if(EventType == FailCreateTrain)
15174  {
15175  LogActionError(45, HeadCode, "", EventType, FrontName);
15176  }
15177  else
15178  {
15179  LogActionError(46, HeadCode, "", EventType, RearName);
15180  }
15181  }
15182  Utilities->CallLogPop(945);
15183  return(false);
15184  }
15185  }
15186  EventType = FailCreatePoints;
15187  if(RearType == Points)
15188  {
15189  if(RearTrackElement.Attribute == 1)
15190  {
15191  if(ReportFlag)
15192  {
15193  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
15194  }
15195  Utilities->CallLogPop(933);
15196  return(false);
15197  }
15198  }
15199  if(FrontType == Points)
15200  {
15201  if(FrontTrackElement.Attribute == 1)
15202  {
15203  if(ReportFlag)
15204  {
15205  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
15206  }
15207  Utilities->CallLogPop(934);
15208  return(false);
15209  }
15210  }
15211 
15212  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
15213  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
15214  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
15215  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
15216  int RouteNumber; //not used
15217  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
15218  {
15219  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
15220  {
15221  EventType = FailEntryRouteSetAgainst;
15222  if(ReportFlag)
15223  {
15224  LogActionError(63, HeadCode, "", EventType, RearName);
15225  }
15226  Utilities->CallLogPop(2317);
15227  return(false);
15228  }
15229  }
15230  Utilities->CallLogPop(939);
15231  return(true);
15232 }
15233 
15234 // ---------------------------------------------------------------------------
15235 
15236 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
15237 {
15238  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
15239  "," + AnsiString(IncDigits));
15240  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
15241  {
15242  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
15243  }
15244  if(!Last2CharactersBothDigits(2, BaseHeadCode))
15245  {
15246  Utilities->CallLogPop(1893);
15247  return(BaseHeadCode);
15248  }
15249  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
15250  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
15251 
15252  while(NextRepeatDigits >= 100)
15253  {
15254  NextRepeatDigits -= 100; // rolls over after 99
15255  }
15256  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
15257 
15258  if(NextRepeatDigitsStr.Length() < 2)
15259  {
15260  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
15261  }
15262  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
15263 
15264  Utilities->CallLogPop(1365);
15265  return(NextRepeatHeadCode);
15266 }
15267 
15268 // ---------------------------------------------------------------------------
15269 
15270 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
15271 {
15272  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
15273  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
15274  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
15275  Utilities->CallLogPop(1366);
15276  return(NextRepeatTime);
15277 }
15278 
15279 // ---------------------------------------------------------------------------
15280 
15281 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
15282 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
15283 {
15284  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
15285  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
15286  int ForwardSecs = int(double(ForwardEventTime) * 86400);
15287  int ReverseSecs = int(double(ReverseEventTime) * 86400);
15288  int RepeatSecs = RepeatMinutes * 60;
15289 
15290  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
15291  {
15292  Utilities->CallLogPop(1367);
15293  return(false);
15294  }
15295  else
15296  {
15297  Utilities->CallLogPop(1368);
15298  return(true);
15299  }
15300 }
15301 
15302 // ---------------------------------------------------------------------------
15303 
15304 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
15305 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
15306 
15307 /* Double crosslink (shuttle) table:
15308 
15309 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
15310  Code ShuttleLink- EntryPtr ShuttleLink-
15311  HeadCode EntryPtr
15312 
15313 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
15314 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
15315 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
15316 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
15317 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
15318 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
15319 
15320 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
15321 */
15322 
15323 {
15324  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
15325  NonRepeatingHeadCode);
15326  int ForwardCount = 0;
15327  int ReverseCount = 0;
15328  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
15329  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
15330  // Forward corresponds to Main, Reverse to Other
15331  TTrainDataEntry *MainTrainDataPtr = 0;
15332  TTrainDataEntry *OtherTrainDataPtr = 0;
15333 
15334  // forward check
15335  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15336  {
15337  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15338  if(TDEntry.HeadCode == MainHeadCode)
15339  {
15340  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15341  {
15342  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15343  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
15344  {
15345  MainTrainDataPtr = &TrainDataVector.at(x);
15346  ForwardEntryPtr = &AVEntry;
15347  ForwardCount++;
15348  ForwardTDVectorNumber = x;
15349  }
15350  }
15351  }
15352  }
15353  if(ForwardCount == 0)
15354  // this is an exception because the headcodes are selected in the same order as the forward check
15355  {
15356  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
15357  }
15358  if(ForwardCount > 1)
15359  {
15360  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
15361  MainHeadCode);
15362  TrainDataVector.clear();
15363  Utilities->CallLogPop(1061);
15364  return(false);
15365  }
15366  // reverse check
15367  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15368  {
15369  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15370  if(TDEntry.HeadCode == NonRepeatingHeadCode)
15371  {
15372  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15373  {
15374  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15375  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
15376  {
15377  OtherTrainDataPtr = &TrainDataVector.at(x);
15378  ReverseCount++;
15379  ReverseEntryPtr = &AVEntry;
15380  ReverseTDVectorNumber = x;
15381  }
15382  }
15383  }
15384  }
15385 
15386  if(ReverseCount == 0)
15387  {
15388  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
15389  TrainDataVector.clear();
15390  Utilities->CallLogPop(1062);
15391  return(false);
15392  }
15393  if(ReverseCount > 1)
15394  {
15395  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
15396  NonRepeatingHeadCode);
15397  TrainDataVector.clear();
15398  Utilities->CallLogPop(1063);
15399  return(false);
15400  }
15401  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
15402  {
15403  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
15404  TrainDataVector.clear();
15405  Utilities->CallLogPop(1064);
15406  return(false);
15407  }
15408  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
15409  {
15410  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
15411  TrainDataVector.clear();
15412  Utilities->CallLogPop(1065);
15413  return(false);
15414  }
15415  if(ForwardEntryPtr->LocationName == "")
15416  {
15417  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
15418  ". One or other service does not have a location set");
15419  TrainDataVector.clear();
15420  Utilities->CallLogPop(1066);
15421  return(false);
15422  }
15423  if(ReverseEntryPtr->LocationName == "")
15424  {
15425  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
15426  ". One or other service does not have a location set");
15427  TrainDataVector.clear();
15428  Utilities->CallLogPop(1067);
15429  return(false);
15430  }
15431  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
15432  {
15433  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
15434  " is at a different location to the referencing train " + MainHeadCode);
15435  TrainDataVector.clear();
15436  Utilities->CallLogPop(1068);
15437  return(false);
15438  }
15439  if(ForwardEntryPtr->Command == "F-nshs")
15440  // i.e. the non repeating link into the shuttle service
15441  {
15442  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
15443  {
15444  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
15445  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
15446  TrainDataVector.clear();
15447  Utilities->CallLogPop(1069);
15448  return(false);
15449  }
15450  }
15451  if(ForwardEntryPtr->Command == "Fns-sh")
15452  // i.e. the non repeating link out from the shuttle service
15453  {
15454  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
15455  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
15456  {
15457  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
15458  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
15459  TrainDataVector.clear();
15460  Utilities->CallLogPop(1070);
15461  return(false);
15462  }
15463  }
15464  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
15465  // i.e. a non repeating link to or from the shuttle service
15466  {
15467  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15468  {
15469  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
15470  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
15471  TrainDataVector.clear();
15472  Utilities->CallLogPop(1071);
15473  return(false);
15474  }
15475  }
15476 /* it's allowed to have a different description
15477  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
15478  {
15479  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
15480  {
15481  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
15482  TrainDataVector.clear();
15483  Utilities->CallLogPop(1072);
15484  return false;
15485  }
15486  }
15487 */
15488  if(ForwardEntryPtr->Command == "Sns-sh")
15489  {
15490  if(ReverseEntryPtr->Command != "F-nshs")
15491  {
15492  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
15493  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
15494  TrainDataVector.clear();
15495  Utilities->CallLogPop(1073);
15496  return(false);
15497  }
15498  }
15499  if(ForwardEntryPtr->Command == "F-nshs")
15500  {
15501  if(ReverseEntryPtr->Command != "Sns-sh")
15502  {
15503  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
15504  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
15505  TrainDataVector.clear();
15506  Utilities->CallLogPop(1074);
15507  return(false);
15508  }
15509  else
15510  {
15511  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15512  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
15513  if(OtherTrainDataPtr->Description == "")
15514  {
15515  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
15516  }
15517  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15518  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15519  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15520  }
15521  }
15522  if(ForwardEntryPtr->Command == "Sns-fsh")
15523  {
15524  if(ReverseEntryPtr->Command != "Fns-sh")
15525  {
15526  SecondPassMessage(GiveMessages,
15527  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
15528  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
15529  TrainDataVector.clear();
15530  Utilities->CallLogPop(1075);
15531  return(false);
15532  }
15533  }
15534  if(ForwardEntryPtr->Command == "Fns-sh")
15535  {
15536  if(ReverseEntryPtr->Command != "Sns-fsh")
15537  {
15538  SecondPassMessage(GiveMessages,
15539  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
15540  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
15541  TrainDataVector.clear();
15542  Utilities->CallLogPop(1076);
15543  return(false);
15544  }
15545  else
15546  {
15547  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
15548  // links to the non-repeating non-shuttle linked service
15549  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15550  // needed for creating formatted timetable
15551  if(OtherTrainDataPtr->Description == "")
15552  {
15553  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
15554  }
15555  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15556  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15557  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15558  }
15559  }
15560  Utilities->CallLogPop(1077);
15561  return(true);
15562 }
15563 
15564 // ---------------------------------------------------------------------------
15565 
15566 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
15567 // Forward train is the finish shuttle entry 'Fns-sh'.
15568 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
15569 {
15570  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
15571  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
15572  int ForwardSecs = int(double(ForwardEventTime) * 86400);
15573  int ReverseSecs = int(double(ReverseEventTime) * 86400);
15574  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
15575 
15576  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
15577  {
15578  Utilities->CallLogPop(1369);
15579  return(false);
15580  }
15581  else
15582  {
15583  Utilities->CallLogPop(1370);
15584  return(true);
15585  }
15586 }
15587 
15588 // ---------------------------------------------------------------------------
15589 
15590 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
15591 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
15592 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
15593 // don't ever need to and as designed would skip repeats.
15594 
15595 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
15596 {
15597  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
15598  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
15599  {
15600  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
15601  }
15602  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
15603  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
15604  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
15605 
15606  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
15607  {
15608  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
15609  TrainDataVector.clear();
15610  Utilities->CallLogPop(1091);
15611  return(false);
15612  }
15613  while(LastActionCommand == "Fns")
15614  {
15615  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
15616  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
15617  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
15618  {
15619  SecondPassMessage(GiveMessages,
15620  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
15621  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
15622  TrainDataVector.clear();
15623  Utilities->CallLogPop(1092);
15624  return(false);
15625  }
15626  }
15627  // exit the 'while' with LastActionCommand FSH-XX
15628  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
15629  {
15630  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
15631  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
15632  ". The linking of two or more shuttles is not permitted.");
15633  TrainDataVector.clear();
15634  Utilities->CallLogPop(1093);
15635  return(false);
15636  }
15637  Utilities->CallLogPop(1094);
15638  return(true);
15639 }
15640 
15641 // ---------------------------------------------------------------------------
15642 
15643 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
15644 {
15645  if(!GiveMessages)
15646  {
15647  return;
15648  }
15649  // if(ServiceReference == "") ShowMessage(Message);
15650  if(!CheckHeadCodeValidity(12, false, ServiceReference))
15651  {
15652  ShowMessage(Message);
15653  }
15654  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
15655  // false means don't give messages within the function
15656  else
15657  {
15658  ShowMessage("Service " + ServiceReference + ": " + Message);
15659  }
15660 }
15661 
15662 // ---------------------------------------------------------------------------
15663 
15664 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
15665 {
15666  if(!GiveMessages)
15667  {
15668  return;
15669  }
15670  ShowMessage(Message);
15671 }
15672 
15673 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
15674 // ---------------------------------------------------------------------------
15675 
15676 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
15677 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
15678 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
15679 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
15680 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
15681 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
15682 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
15683 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
15684 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
15685 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
15686 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
15687 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
15688 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
15689 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
15690 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
15691 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
15692 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
15693 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
15694 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
15695 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
15696 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
15697 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
15698 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
15699 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
15700 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
15701 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
15702 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
15703 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
15704 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
15705 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
15706 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5 //added at v2.9.1
15707 {
15708  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
15709  AnsiString(ActionEventType) + "," + LocationID);
15710  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
15711 
15712  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
15713  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode; //added at v2.9.1 to give more info to user
15714 
15715  Prefix = " ERROR: ";
15716  if(ActionEventType == FailTrainEntry)
15717  {
15718  Prefix = " HELD: ";
15719  ErrorLog = " can't enter railway, train obstructing entry position ";
15720  WarningStr = " can't enter railway, train obstructing entry position ";
15721  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
15722  }
15723  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
15724  {
15725  Prefix = " HELD: ";
15726  ErrorLog = " can't enter railway, route set against it at entry position ";
15727  WarningStr = " can't enter railway, route set against it at entry position ";
15728  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
15729  }
15730  else if(ActionEventType == FailCreateTrain)
15731  {
15732  Prefix = " HELD: ";
15733  ErrorLog = " can't be created, train obstructing ";
15734  WarningStr = " can't be created, train obstructing ";
15735  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
15736  }
15737  else if(ActionEventType == FailCreateLockedRoute)
15738  {
15739  Prefix = " HELD: ";
15740  ErrorLog = " can't be created on a locked route at ";
15741  WarningStr = " can't be created on a locked route at ";
15742  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
15743  }
15744  else if(ActionEventType == FailEnterLockedRoute)
15745  {
15746  Prefix = " HELD: ";
15747  ErrorLog = " can't enter on a locked route at ";
15748  WarningStr = " can't enter on a locked route at ";
15749  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
15750  }
15751  else if(ActionEventType == FailCreatePoints)
15752  {
15753  Prefix = " HELD: ";
15754  ErrorLog = " can't be created, diverging points at ";
15755  WarningStr = " can't be created, diverging points at ";
15756  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
15757  }
15758  else if(ActionEventType == FailUnexpectedExitRailway)
15759  {
15760  ErrorLog = " left railway unexpectedly at ";
15761  UnexpectedExits++;
15762  }
15763  else if(ActionEventType == FailIncorrectExit)
15764  {
15765  ErrorLog = " left railway at an incorrect exit at ";
15766  IncorrectExits++;
15767  }
15768  else if(ActionEventType == FailLocTooShort)
15769  {
15770  ErrorLog = " failed to split - location too short at ";
15771  WarningStr = " failed to split, location too short at ";
15772  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
15773  }
15774  else if(ActionEventType == FailSplitDueToOtherTrain)
15775  {
15776  Prefix = " HELD: ";
15777  ErrorLog = " unable to split - other train obstructing at ";
15778  WarningStr = " unable to split - other train obstructing at ";
15779  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
15780  }
15781  else if(ActionEventType == FailUnexpectedBuffers)
15782  {
15783  ErrorLog = " stopped at buffers unexpectedly at position ";
15784  }
15785  else if(ActionEventType == FailMissedArrival)
15786  {
15787  ErrorLog = " failed to stop at ";
15788  MissedStops++;
15789  }
15790  else if(ActionEventType == FailMissedSplit)
15791  {
15792  ErrorLog = " failed to split at ";
15794  }
15795  else if(ActionEventType == FailMissedJBO)
15796  {
15797  ErrorLog = " failed to be joined by other train at ";
15799  }
15800  else if(ActionEventType == FailMissedJoinOther)
15801  {
15802  ErrorLog = " failed to join other train at ";
15804  }
15805  else if(ActionEventType == FailMissedTerminate)
15806  {
15807  ErrorLog = " failed to terminate at ";
15809  }
15810  else if(ActionEventType == FailMissedNewService)
15811  {
15812  ErrorLog = " failed to form new service at ";
15814  }
15815  else if(ActionEventType == FailMissedExitRailway)
15816  {
15817  ErrorLog = " failed to exit railway ";
15819  }
15820  else if(ActionEventType == FailMissedChangeDirection)
15821  {
15822  ErrorLog = " failed to change direction at ";
15823 // OtherMissedEvents++; //dropped at v2.12.0 as cdt shouldn't count
15824  }
15825  else if(ActionEventType == FailMissedPass)
15826  {
15827  ErrorLog = " failed to pass ";
15828 // OtherMissedEvents++; //dropped at v2.12.0 as missed pass shouldn't count
15829  }
15830  else if(ActionEventType == FailBuffersPreventingStart)
15831  {
15832  ErrorLog = " facing buffers and unable to start at ";
15833  }
15834  else if(ActionEventType == FailDerailed)
15835  {
15836  ErrorLog = " DERAILED at position ";
15837  Prefix = " DERAILMENT: ";
15838  Derailments++;
15839  }
15840  else if(ActionEventType == FailBufferCrash)
15841  {
15842  ErrorLog = " CRASHED INTO BUFFERS at ";
15843  Prefix = " CRASH: ";
15844  CrashedTrains++;
15845  }
15846  else if(ActionEventType == FailLevelCrossingCrash)
15847  {
15848  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
15849  Prefix = " CRASH: ";
15850  CrashedTrains++;
15851  }
15852  else if(ActionEventType == FailCrashed)
15853  {
15854  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
15855  Prefix = " CRASH: ";
15856  CrashedTrains++;
15857  CrashedTrains++;
15858  }
15859  else if(ActionEventType == FailSPAD)
15860  {
15861  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
15862  Prefix = " SPAD: ";
15863  SPADEvents++;
15864  }
15865  else if(ActionEventType == FailLockedRoute)
15866  {
15867  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
15868  Prefix = " SPAD RISK: ";
15869  SPADRisks++;
15870  }
15871  else if(ActionEventType == RouteForceCancelled)
15872  {
15873  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
15874  }
15875  else if(ActionEventType == WaitingForJBO)
15876  {
15877  Prefix = " WARNING: ";
15878  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
15879  WarningStr = " waiting to join " + OtherHeadCode + " at ";
15880  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
15881  }
15882  else if(ActionEventType == WaitingForFJO)
15883  {
15884  Prefix = " WARNING: ";
15885  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
15886  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
15887  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
15888  }
15889 
15890  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
15891  PerfLogForm->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
15892  Utilities->CallLogPop(1371);
15893 }
15894 
15895 // ---------------------------------------------------------------------------
15896 
15898 {
15899 /* //for testing purposes
15900  TrainDataEntry
15901  AnsiString HeadCode, Description;//null on creation
15902  int StartSpeed, MaxRunningSpeed;//both kph
15903  int RepeatNumber;
15904  TActionVector ActionVector;
15905  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
15906  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
15907 
15908  ActionVectorEntry
15909  TTimetableEntryType FormatType;
15910  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
15911  AnsiString LocationName, Command, OtherHeadCode;//null on creation
15912  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
15913  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15914  int RepeatNumber;
15915 
15916  TrainOperatingData
15917  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
15918  int TrainID;
15919  TRunningEntry RunningEntry;
15920  TDateTime StartTime;
15921  AnsiString HeadCode;
15922 */
15923  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
15924  std::ofstream OutFile("TrainData.csv");
15925 
15926  if(OutFile == 0)
15927  {
15928  ShowMessage("Output file TrainData.csv failed to open");
15929  Utilities->CallLogPop(1372);
15930  return;
15931  }
15932  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15933  {
15934  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15935  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
15936 
15937  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
15938  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
15939 
15940  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
15941  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
15942  "RepeatNumber" << '\n' << '\n';
15943  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15944  {
15945  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15946  AnsiString TimetableEntryTypeStr;
15947  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
15948  switch(AVEntry.FormatType)
15949  {
15950  case 0:
15951  {
15952  TimetableEntryTypeStr = "NoFormat";
15953  break;
15954  }
15955 
15956  case 1:
15957  {
15958  TimetableEntryTypeStr = "TimeLoc";
15959  break;
15960  }
15961 
15962  case 2:
15963  {
15964  TimetableEntryTypeStr = "TimeTimeLoc";
15965  break;
15966  }
15967 
15968  case 3:
15969  {
15970  TimetableEntryTypeStr = "TimeCmd";
15971  break;
15972  }
15973 
15974  case 4:
15975  {
15976  TimetableEntryTypeStr = "StartNew";
15977  break;
15978  }
15979 
15980  case 5:
15981  {
15982  TimetableEntryTypeStr = "TimeCmdHeadCode";
15983  break;
15984  }
15985 
15986  case 6:
15987  {
15988  TimetableEntryTypeStr = "FinRemHere";
15989  break;
15990  }
15991 
15992  case 7:
15993  {
15994  TimetableEntryTypeStr = "FNSShuttle";
15995  break;
15996  }
15997 
15998  case 8:
15999  {
16000  TimetableEntryTypeStr = "SNTShuttle";
16001  break;
16002  }
16003 
16004  case 9:
16005  {
16006  TimetableEntryTypeStr = "SNSShuttle";
16007  break;
16008  }
16009 
16010  case 10:
16011  {
16012  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
16013  break;
16014  }
16015 
16016  case 11:
16017  {
16018  TimetableEntryTypeStr = "FSHNewService";
16019  break;
16020  }
16021 
16022  case 12:
16023  {
16024  TimetableEntryTypeStr = "Repeat";
16025  break;
16026  }
16027 
16028  default:
16029  {
16030  TimetableEntryTypeStr = "Default";
16031  break;
16032  }
16033  }
16034  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
16035  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
16036  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
16037  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
16038  AVEntry.NumberOfRepeats << '\n';
16039  }
16040  OutFile << '\n';
16041  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
16042  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
16043  {
16044  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
16045  AnsiString RunningEntryStr;
16046  // NotStarted, Running, Exited
16047  switch(TOD.RunningEntry)
16048  {
16049  case 0:
16050  {
16051  RunningEntryStr = "NotStarted";
16052  break;
16053  }
16054 
16055  case 1:
16056  {
16057  RunningEntryStr = "Running";
16058  break;
16059  }
16060 
16061  case 2:
16062  {
16063  RunningEntryStr = "Exited";
16064  break;
16065  }
16066  }
16067  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
16068  }
16069  OutFile << '\n';
16070  }
16071  OutFile.close();
16072  Utilities->CallLogPop(1373);
16073 }
16074 
16075 // ---------------------------------------------------------------------------
16076 
16077 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
16078 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
16079 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
16080 {
16081  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
16082  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
16084  ShowMessage(Message);
16085  BaseTime = TDateTime::CurrentDateTime();
16086  StopTTClockFlag = false;
16087  Utilities->CallLogPop(1374);
16088 }
16089 
16090 // ---------------------------------------------------------------------------
16091 
16092 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
16093 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
16094 // from the start of the relevant vectors. Can't save the pointer values
16095 // as these will be different each time the vectors are created
16096 {
16097  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
16098  Utilities->SaveFileInt(SessionFile, TrainVector.size());
16099  for(unsigned int x = 0; x < TrainVector.size(); x++)
16100  {
16101  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
16102  }
16103  Utilities->CallLogPop(1375);
16104 }
16105 
16106 // ---------------------------------------------------------------------------
16107 
16108 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
16109 {
16110  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
16111  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
16112  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
16113  // by zero error in calculating AValue, use 1
16114  for(int x = 0; x < NumberOfTrains; x++)
16115  {
16116  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
16117  // by zero error in calculating AValue, use 1
16118  NewTrain->LoadOneSessionTrain(0, SessionFile);
16119  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
16120  // added at v2.4.0. have to include as that value not stored in session file
16121  {
16122  NewTrain->StoppedWithoutPower = true;
16123  }
16124  TrainVector.push_back(*NewTrain);
16125  LastTrainLoaded = x;
16126  }
16127  delete NewTrain;
16128  Utilities->CallLogPop(1376);
16129 }
16130 
16131 // ---------------------------------------------------------------------------
16132 
16133 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
16134 {
16135  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
16136  int NumberOfTrains;
16137 
16138  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
16139  {
16140  Utilities->CallLogPop(1377);
16141  return(false);
16142  }
16143  for(int x = 0; x < NumberOfTrains; x++)
16144  {
16145  if(!(TTrain::CheckOneSessionTrain(InFile)))
16146  {
16147  Utilities->CallLogPop(1378);
16148  return(false);
16149  }
16150  }
16151  Utilities->CallLogPop(1379);
16152  return(true);
16153 }
16154 
16155 // ---------------------------------------------------------------------------
16156 
16157 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
16158 {
16159  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
16160  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
16161  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
16162  {
16163  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
16164  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
16165  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
16166  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
16167  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
16168  }
16169  Utilities->CallLogPop(1380);
16170 }
16171 
16172 // ---------------------------------------------------------------------------
16173 
16174 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
16175 {
16176  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
16177  TAllRoutes::TLockedRouteClass LockedRouteObject;
16178  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
16179 
16180  for(int x = 0; x < LockedRouteVectorSize; x++)
16181  {
16182  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
16183  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
16184  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
16185  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
16186  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
16187  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
16188  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
16189  }
16190  Utilities->CallLogPop(1381);
16191 }
16192 
16193 // ---------------------------------------------------------------------------
16194 
16195 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
16196 {
16197  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
16198  int LockedRouteVectorSize;
16199 
16200  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
16201  {
16202  Utilities->CallLogPop(1382);
16203  return(false);
16204  }
16205  for(int x = 0; x < LockedRouteVectorSize; x++)
16206  {
16207  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
16208  {
16209  Utilities->CallLogPop(1383);
16210  return(false);
16211  }
16212  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
16213  {
16214  Utilities->CallLogPop(1384);
16215  return(false);
16216  }
16217  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
16218  {
16219  Utilities->CallLogPop(1385);
16220  return(false);
16221  }
16222  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
16223  {
16224  Utilities->CallLogPop(1386);
16225  return(false);
16226  }
16227  if(!Utilities->CheckFileDouble(SessionFile))
16228  {
16229  Utilities->CallLogPop(1387);
16230  return(false);
16231  }
16232  }
16233  Utilities->CallLogPop(1388);
16234  return(true);
16235 }
16236 
16237 // ---------------------------------------------------------------------------
16238 
16239 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
16240 {
16241  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
16242  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
16243  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
16244  {
16245  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
16246  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
16247  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
16248  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
16249  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
16250  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
16251  }
16252  Utilities->CallLogPop(1389);
16253 }
16254 
16255 // ---------------------------------------------------------------------------
16256 
16257 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
16258 {
16259  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
16260  TContinuationAutoSigEntry ContinuationAutoSigObject;
16261  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
16262 
16263  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
16264  {
16265  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
16266  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
16267  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
16268  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
16269  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
16270  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
16271  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
16272  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
16273  }
16274  Utilities->CallLogPop(1390);
16275 }
16276 
16277 // ---------------------------------------------------------------------------
16278 
16279 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
16280 {
16281  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
16282  int ContinuationAutoSigVectorSize;
16283 
16284  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
16285  {
16286  Utilities->CallLogPop(1391);
16287  return(false);
16288  }
16289  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
16290  {
16291  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
16292  {
16293  Utilities->CallLogPop(1392);
16294  return(false);
16295  }
16296  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
16297  {
16298  Utilities->CallLogPop(1393);
16299  return(false);
16300  }
16301  if(!Utilities->CheckFileDouble(SessionFile))
16302  {
16303  Utilities->CallLogPop(1405);
16304  return(false);
16305  }
16306  if(!Utilities->CheckFileDouble(SessionFile))
16307  {
16308  Utilities->CallLogPop(1406);
16309  return(false);
16310  }
16311  if(!Utilities->CheckFileDouble(SessionFile))
16312  {
16313  Utilities->CallLogPop(1407);
16314  return(false);
16315  }
16316  if(!Utilities->CheckFileDouble(SessionFile))
16317  {
16318  Utilities->CallLogPop(1394);
16319  return(false);
16320  }
16321  }
16322  Utilities->CallLogPop(1395);
16323  return(true);
16324 }
16325 
16326 // ---------------------------------------------------------------------------
16327 
16328 /*
16329  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
16330  {
16331  public:
16332  AnsiString Description; ///< service description
16333  AnsiString HeadCode; ///< service headcode
16334  int RepeatNumber; ///< service RepeatNumber
16335  int IncrementalMinutes; ///< Repeat separation in minutes
16336  int IncrementalDigits; ///< Repeat headcode separation
16337  int VectorPosition; ///< TrackVectorPosition for the continuation element
16338  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
16339  };
16340 
16341 
16342  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
16343  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
16344 */
16345 
16347 // build this into timetable load so session loading can use it too
16348 // being a multimap it automatically sorts in ascending EventTime order
16349 {
16350  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
16352  // need to clear as this called twice when load a session
16353  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16354  {
16355  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
16356  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
16357  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
16358 
16359  if(AVFirstEntry.Command == "Snt")
16360  // new train (no need to include Snt-sh since they can't start at a continuation)
16361  {
16364  {
16366  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
16367  // retains this value for all repeats
16368  CTEEntry.RepeatNumber = 0; // for first entry
16369  CTEEntry.TrainDataEntryPtr = &TDEntry;
16370  // retains this value for all repeats
16371  CTEEntry.HeadCode = TDEntry.HeadCode;
16372  CTEEntry.Description = TDEntry.Description;
16373  CTEEntry.IncrementalMinutes = 0;
16374  CTEEntry.IncrementalDigits = 0;
16375  if(AVLastEntry.FormatType == Repeat)
16376  {
16377  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
16378  // retains this value or 0 for all repeats
16379  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
16380  // retains this value or 0 for all repeats
16381  }
16382  CTEMMP.first = AVFirstEntry.EventTime;
16383  CTEMMP.second = CTEEntry;
16384  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
16385  // base entry
16386  if(TDEntry.NumberOfTrains > 1)
16387  {
16388  if(AVLastEntry.FormatType != Repeat)
16389  {
16390  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
16391  }
16392  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
16393  {
16394  CTEEntry.RepeatNumber = y;
16395  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
16396  // CTEEntry.VectorPosition stays same
16397  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
16398  CTEMMP.second = CTEEntry;
16399  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
16400  }
16401  }
16402  }
16403  }
16404  }
16405  Utilities->CallLogPop(1396);
16406 }
16407 
16408 // ---------------------------------------------------------------------------
16409 
16411 {
16412  // called when WarningFlashCount == 0 or when press zoomout button
16413  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
16414  if(!Display->ZoomOutFlag)
16415  {
16416  Utilities->CallLogPop(1156);
16417  return;
16418  }
16419  for(unsigned int x = 0; x < TrainVector.size(); x++)
16420  {
16421  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
16422  // if OldPlotElement[x] == -1 then ignore (not plotted)
16424  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
16425  }
16426  Display->Update();
16427  // need to keep this since Update() not called for PlotSmallOutput as too slow
16428  Utilities->CallLogPop(742);
16429 }
16430 
16431 // ---------------------------------------------------------------------------
16432 
16433 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
16434 {
16435  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
16436  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
16437  {
16438  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
16439  }
16440  Utilities->CallLogPop(740);
16441  return(TrainVector.at(VecPos));
16442 }
16443 
16444 // ---------------------------------------------------------------------------
16445 
16446 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
16447 {
16448  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
16449  AnsiString RetStr = "", PartStr = "";
16450 
16451 
16452 /*
16453  Have description & mass etc for train at top - header, then array of actions
16454 
16455  class TActionVectorEntry
16456  {
16457  public:
16458  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
16460  bool SignallerControl;
16462  bool Warning;
16464  int NumberOfRepeats;
16466  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
16468  TDateTime EventTime, ArrivalTime, DepartureTime;
16470  TNumList ExitList;
16472  TTimetableFormatType FormatType;
16474  TTimetableLocationType LocationType;
16476  TTimetableSequenceType SequenceType;
16478  TTimetableShuttleLinkType ShuttleLinkType;
16480  TTrainDataEntry *LinkedTrainEntryPtr;
16482  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
16484 
16485  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
16486 
16487  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
16488 
16489  class TTrainOperatingData
16490  {
16491  public:
16492  int TrainID;
16493  TActionEventType EventReported;
16494  TRunningEntry RunningEntry;
16495 
16496  //inline function
16497  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
16498  };
16499 
16500  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
16501 
16502  class TTrainDataEntry
16503  {
16504  public:
16505  AnsiString HeadCode, ServiceReference, Description;
16507  double MaxBrakeRate;
16509  double MaxRunningSpeed;
16511  double PowerAtRail;
16513  int Mass;
16515  int NumberOfTrains;
16517  int SignallerSpeed;
16519  int StartSpeed;
16521  TActionVector ActionVector;
16523  TTrainOperatingDataVector TrainOperatingDataVector;
16525 
16526  //inline function
16527  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
16528  };
16529 
16530  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
16531 
16532  //formatted timetable types
16533  class TOneTrainFormattedEntry
16534  {
16535  AnsiString Action;//includes location if relevanr
16536  AnsiString Time;
16537  };
16538 
16539  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
16540 
16541  class TOneCompleteFormattedTrain//headcode + list of actions
16542  {
16543  public:
16544  AnsiString HeadCode;
16545  TOneFormattedTrainVector OneFormattedTrainVector;
16546  };
16547 
16548  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
16549 
16550  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
16551  {
16552  public:
16553  AnsiString Header;//description, mass, power, brake rate etc
16554  int NumberOfTrains;// number of repeats + 1
16555  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
16556  };
16557 
16558 
16559  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
16560  //end of formatted timetable types
16561 
16562 */
16563 
16564  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16565 
16566  // format "16/06/2009 20:55:17"
16567  // avoid characters in filename:= / \ : * ? " < > |
16568  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
16569 
16570  AnsiString ShortTTName = "";
16571 
16572  for(int x = TTFileName.Length(); x > 0; x--)
16573  {
16574  if(TTFileName[x] == '\\')
16575  {
16576  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
16577  break;
16578  }
16579  }
16580 
16581  ShowMessage("Creates two timetables named " + ShortTTName +
16582  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
16583 
16584  Screen->Cursor = TCursor(-11); // Hourglass
16585 
16586  AnsiString FormatNoDPStr = "#######0";
16587  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
16588 
16590  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
16591  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
16592 
16593  // all timetable in formatted form
16594  //create the AllTTTrains vector
16595  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16596  {
16597  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
16598  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
16599  if(TrainDataEntry.Mass > 0)
16600  {
16601  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
16602  }
16603  if(TrainDataEntry.PowerAtRail > 0)
16604  {
16605  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
16606  }
16607  if(TrainDataEntry.MaxBrakeRate > 0)
16608  {
16609  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
16610  }
16611  if(TrainDataEntry.MaxRunningSpeed > 0)
16612  {
16613  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
16614  }
16615  FirstHeadCode = TrainDataEntry.HeadCode;
16616  int IncDigits = 0, IncMinutes = 0;
16617  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16618  if(!ActionVector.empty())
16619  {
16620  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
16621  {
16622  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
16623  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16624  }
16625  }
16626  TTrainFormattedInformation OneTTLine;
16627  // contains all information for a single TT entry (including repeats)
16628  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
16629  {
16630  OneTTLine.Header = "";
16631  if((TrainDataEntry.Description != "") && (MassStr != ""))
16632  {
16633  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
16634  }
16635  else if(TrainDataEntry.Description != "")
16636  {
16637  OneTTLine.Header = TrainDataEntry.Description;
16638  }
16639  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
16640  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
16641  for(unsigned int z = 0; z < ActionVector.size(); z++)
16642  {
16643  TOneTrainFormattedEntry OneTTEntry;
16644  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
16645  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
16646  AnsiString PartStr = "", TimeStr = "";
16647 /*
16648  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
16649  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
16650  ExitRailway};
16651  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
16652  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
16653  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
16654 */
16655  if(ActionVectorEntry.SequenceType == Start)
16656  {
16657  if(ActionVectorEntry.FormatType == StartNew)
16658  {
16659  if(ActionVectorEntry.LocationName != "")
16660  {
16661  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
16662  {
16663  PartStr = "Enters at " + ActionVectorEntry.LocationName;
16664  }
16665  else
16666  {
16667  PartStr = "Created at " + ActionVectorEntry.LocationName;
16668  }
16669  }
16670  else // may be a named continuation or other element, and if so report that
16671  {
16672  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
16673  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
16674  {
16675  if(LocName != "")
16676  {
16677  PartStr = "Enters at " + LocName;
16678  }
16679  else // use rear position if it's a continuation
16680  {
16681  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
16682  }
16683  }
16684  else // not a continuation
16685  {
16686  if(LocName != "")
16687  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
16688  // but include anyway
16689  {
16690  PartStr = "Created at " + LocName;
16691  }
16692  else // use rear position again
16693  {
16694  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
16695  }
16696  }
16697  }
16698  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
16699  }
16700  else if(ActionVectorEntry.FormatType == SNTShuttle)
16701  {
16702  if(y == 0) // first train
16703  {
16704  PartStr = "Enters at " + ActionVectorEntry.LocationName;
16705  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
16706  }
16707  else
16708  {
16709  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
16710  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
16711  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
16712  } // y-1 for headcode above since it is the last repeat value that the train is from
16713 
16714  }
16715  else if(ActionVectorEntry.Command == "Sfs")
16716  {
16717  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
16718  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16719  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
16720  }
16721  else if(ActionVectorEntry.Command == "Sns")
16722  {
16723  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
16724  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16725  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
16726  }
16727  else if(ActionVectorEntry.FormatType == SNSShuttle)
16728  {
16729  if(y == 0) // first entry from shuttle
16730  {
16731  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
16732  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
16733  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
16734  }
16735  else
16736  {
16737  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
16738  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
16739  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
16740  } // y-1 for headcode above since it is the last repeat value that the train is from
16741 
16742  }
16743  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
16744  {
16745  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
16746  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
16747  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
16748  AnsiString FirstHeadCode = TDE->HeadCode;
16749  int LastRepeatNumber = TDE->NumberOfTrains - 1;
16750  // a shuttle has to have at least 1 repeat
16751  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
16752  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
16753  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
16754  }
16755  }
16756  else if(ActionVectorEntry.SequenceType == Intermediate)
16757  {
16758  if(ActionVectorEntry.FormatType == TimeTimeLoc)
16759  {
16760  // here need 2 entries if times different so push the first right away & the second later
16761  // if times same just give the arrival entry
16762  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
16763  {
16764  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
16765  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
16766  OneTTEntry.Action = PartStr;
16767  OneTTEntry.Time = TimeStr;
16768  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
16769  PartStr = "Departs from " + ActionVectorEntry.LocationName;
16770  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
16771  }
16772  else
16773  {
16774  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
16775  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
16776  }
16777  }
16778  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
16779  {
16780  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
16781  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
16782  }
16783  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
16784  {
16785  PartStr = "Departs from " + ActionVectorEntry.LocationName;
16786  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
16787  }
16788  else if(ActionVectorEntry.FormatType == PassTime)
16789  {
16790  PartStr = "Passes " + ActionVectorEntry.LocationName;
16791  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
16792  }
16793  else if(ActionVectorEntry.Command == "jbo")
16794  {
16795  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
16796  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16797  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
16798  }
16799  else if(ActionVectorEntry.Command == "fsp")
16800  {
16801  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
16802  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16803  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
16804  }
16805  else if(ActionVectorEntry.Command == "rsp")
16806  {
16807  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
16808  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16809  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
16810  }
16811  else if(ActionVectorEntry.Command == "cdt")
16812  {
16813  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
16814  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
16815  }
16816  }
16817  else if(ActionVectorEntry.SequenceType == Finish)
16818  {
16819  if(ActionVectorEntry.Command == "Fns")
16820  {
16821  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16822  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16823  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
16824  }
16825  else if(ActionVectorEntry.Command == "F-nshs")
16826  {
16827  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16828  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
16829  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
16830  }
16831  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
16832  {
16833  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
16834  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
16835  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
16836  // y+1 because it's the NEXT service repeat number that is relevant
16837  }
16838  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
16839  {
16840  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16841  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
16842  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
16843  }
16844  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
16845  {
16846  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16847  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
16848  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
16849  // y+1 because it's the NEXT service repeat number that is relevant
16850  }
16851  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
16852  {
16853  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
16854  // only used in chronological tt
16855  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
16856  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
16857  }
16858  else if(ActionVectorEntry.Command == "Frh")
16859  {
16860  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
16861  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
16862  if(z > 0)
16863  // should be for finish entry but include check for safety
16864  {
16865  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
16866  {
16867  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
16868  }
16869  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
16870  {
16871  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
16872  }
16873  else
16874  {
16875  TimeStr = " "; // shouldn't ever get here
16876  }
16877  }
16878  }
16879  else if(ActionVectorEntry.Command == "Fer")
16880  {
16881  AnsiString AllowedExits;
16882  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
16883  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
16884  }
16885  else if(ActionVectorEntry.Command == "Fjo")
16886  {
16887  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
16888  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16889  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
16890  }
16891  }
16892  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
16893  {
16894  continue; // no entry needed for a repeat
16895  }
16896  OneTTEntry.Action = PartStr;
16897  OneTTEntry.Time = TimeStr;
16898  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
16899  // one per action
16900  }
16901  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
16902  // one per repeat
16903  }
16904  AllTTTrains->push_back(OneTTLine); // one per repeating train
16905  }
16906  // AllTTTrains vector now complete
16907 
16908  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
16909 
16910  if(TTFile == 0)
16911  {
16912  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
16913  delete AllTTTrains;
16914  Utilities->CallLogPop(1567);
16915  return;
16916  }
16917 /* formatted timetable types
16918  class TOneTrainFormattedEntry
16919  {
16920  AnsiString Action;//includes location if relevant
16921  AnsiString Time;
16922  };
16923 
16924  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
16925 
16926  class TOneCompleteFormattedTrain//headcode + list of actions
16927  {
16928  public:
16929  AnsiString HeadCode;
16930  TOneFormattedTrainVector OneFormattedTrainVector;
16931  };
16932 
16933  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
16934 
16935  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
16936  {
16937  public:
16938  AnsiString Header;//description, mass, power, brake rate etc
16939  int NumberOfTrains;// number of repeats + 1
16940  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
16941  };
16942 
16943  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
16944  //end of formatted timetable types
16945 */
16946 
16947  // new layout using multiple rows
16948  TTFile << TableTitle.c_str() << '\n' << '\n';
16949  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
16950  {
16951  TTFile << AllTTTrains->at(x).Header.c_str();
16952  TTFile << '\n';
16953  TTFile << ','; // for the blank line above the action list
16954  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16955  {
16956  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
16957  {
16958  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
16959  }
16960  else
16961  {
16962  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
16963  }
16964  }
16965  TTFile << '\n' << '\n';
16966 
16967  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
16968  {
16969  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
16970  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16971  {
16972  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
16973  {
16974  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
16975  }
16976  else
16977  {
16978  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
16979  }
16980  }
16981  TTFile << '\n';
16982  }
16983  TTFile << '\n' << '\n';
16984  }
16985 
16986  TTFile.close();
16987 
16988  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16989 
16990  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16991 
16992  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
16993 
16994  if(TTFile2 == 0)
16995  {
16996  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
16997  delete AllTTTrains;
16998  Utilities->CallLogPop(1710);
16999  return;
17000  }
17001  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
17002  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
17003  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
17004 
17005  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
17006  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
17007 
17008  // multimap of AnsiStrings with TimeString as key (to sort automatically)
17009 
17010  TTFile2 << TableTitle.c_str() << '\n' << '\n';
17011  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
17012  {
17013  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17014  {
17015  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
17016  {
17017  bool GiveMessagesFalse = false;
17018  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
17019  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
17020  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
17021  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
17022  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
17023  {
17024  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
17025  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
17026  TimeString = TimeString.SubString(9, 5);
17027  ActionString += " " + OtherHeadCode;
17028  }
17029  if(TimeString.SubString(1, 7) == "End at ")
17030  // for Frh-sh final entry
17031  {
17032  TimeString = TimeString.SubString(8, 5);
17033  }
17034  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
17035  AnsiMultiMapEntry.first = TimeString;
17036  AnsiMultiMapEntry.second = OneLine;
17037  TAMM->insert(AnsiMultiMapEntry);
17038  }
17039  }
17040  }
17041 
17042  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
17043  {
17044  TTFile2 << (AMMIT->second).c_str();
17045  }
17046  delete AllTTTrains;
17047  delete TAMM;
17048  TTFile2.close();
17049  Utilities->CallLogPop(1580);
17050 }
17051 
17052 // ---------------------------------------------------------------------------
17053 
17054 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
17055  bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
17056 {
17057 
17058  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
17059  bool AnalysisError = false;
17060  AnsiString SequenceLog = "SequenceLog\n";
17061 
17062 /* Double crosslink (shuttle) table:
17063 
17064 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
17065  Code ShuttleLink- EntryPtr ShuttleLink-
17066  HeadCode EntryPtr
17067 
17068 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
17069 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
17070 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
17071 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
17072 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
17073 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
17074 
17075 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
17076 */
17077 
17078  try
17079  {
17080  //New section at v2.5.0 for tt conflict analysis
17081  /*
17082  typedef std::list<AnsiString> TServiceCallingLocsList;
17083  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
17084 
17086  struct TLocServiceTimes
17087  {
17088  AnsiString Location;
17089  AnsiString ServiceAndRepeatNum;
17090  AnsiString AtLocTime;
17091  AnsiString ArrTime;
17092  AnsiString DepTime;
17093  AnsiString FrhMarker;
17094  };
17095  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
17096  */
17097 
17098  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
17099  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
17100 
17101  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
17102  TTrainDataVector::iterator TDVIt, TDVCopyIt;
17103  int Suffix = 0;
17104  int IteratorNumber = 0;
17105  AnsiString AnsiSuffix = "";
17106  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
17107  {
17108  IteratorNumber++; //first value in loop is 1
17109  Suffix = 0;
17110  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
17111  {
17112  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
17113  {
17114  Suffix++; //first value is 1
17115  AnsiSuffix = AnsiString(Suffix);
17116  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
17117  }
17118  }
17119  }
17120  SequenceLog += "1\n";
17121  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
17122  TServiceCallingLocsList ServiceCallingLocsList;
17123  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
17124  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17125  {
17126  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
17127  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
17128  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
17129  ServiceCallingLocsList.clear();
17130  if(ActionVector.empty())
17131  {
17132  continue;
17133  }
17134  if(ActionVector.at(0).SignallerControl)
17135  {
17136  continue;
17137  }
17138  for(unsigned int z = 0; z < ActionVector.size(); z++)
17139  {
17140  TActionVectorEntry AVE = ActionVector.at(z);
17141  if(AVE.FormatType == StartNew)
17142  {
17143  if(AVE.LocationType == AtLocation) //located Snt
17144  {
17145  ServiceCallingLocsList.push_back(AVE.LocationName);
17146  }
17147  else //unlocated Snt (could be entering at continuation)
17148  {
17150  if(TE.ActiveTrackElementName != "")
17151  {
17152  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
17153  }
17154  else
17155  {
17156  int HLoc = TE.HLoc;
17157  int VLoc = TE.VLoc;
17158  AnsiString HString;
17159  AnsiString VString;
17160  if(HLoc < 0)
17161  {
17162  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
17163  }
17164  else
17165  {
17166  HString = AnsiString(HLoc);
17167  }
17168  if(VLoc < 0)
17169  {
17170  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
17171  }
17172  else
17173  {
17174  VString = AnsiString(VLoc);
17175  }
17176  ServiceCallingLocsList.push_back(HString + '-' + VString);
17177  }
17178  }
17179  }
17180  else if(AVE.SequenceType == Start) //other start entries, all located
17181  {
17182  ServiceCallingLocsList.push_back(AVE.LocationName);
17183  }
17184  else if(AVE.FormatType == TimeLoc) //z must be > 0
17185  {
17186  if(ServiceCallingLocsList.back() != AVE.LocationName)
17187  {
17188  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
17189  }
17190  }
17191  else if(AVE.FormatType == PassTime)
17192  {
17193  ServiceCallingLocsList.push_back(AVE.LocationName);
17194  }
17195  else if(AVE.FormatType == TimeTimeLoc)
17196  {
17197  ServiceCallingLocsList.push_back(AVE.LocationName);
17198  }
17199  else if(AVE.Command == "cdt") //list if not next to start or finish
17200  {
17201  if(ActionVector.at(z-1).SequenceType == Start)
17202  {
17203  continue;
17204  }
17205  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
17206  {
17207  continue;
17208  }
17209  else
17210  {
17211  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
17212  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
17213  }
17214  }
17215  else if(AVE.FormatType == ExitRailway) //Fer
17216  {
17217  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
17218  AnsiString LName = TE.ActiveTrackElementName;
17219  if(LName != "")
17220  {
17221  ServiceCallingLocsList.push_back(LName);
17222  }
17223  else
17224  {
17225  int HLoc = TE.HLoc;
17226  int VLoc = TE.VLoc;
17227  AnsiString HString;
17228  AnsiString VString;
17229  if(HLoc < 0)
17230  {
17231  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
17232  }
17233  else
17234  {
17235  HString = AnsiString(HLoc);
17236  }
17237  if(VLoc < 0)
17238  {
17239  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
17240  }
17241  else
17242  {
17243  VString = AnsiString(VLoc);
17244  }
17245  ServiceCallingLocsList.push_back(HString + '-' + VString);
17246  }
17247  }
17248  }
17249  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
17250  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
17251  }
17252  //AllServiceCallingLocsMap built
17253  SequenceLog += "2\n";
17254 /* this sequence is to test the validity of AllServiceCallingLocsMap
17255  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
17256  std::ofstream Test(TestFile.c_str());
17257 
17258  if(TestFile == 0)
17259  {
17260  ShowMessage("TestFile failed to open - can't be created");
17261  Utilities->CallLogPop();
17262  return false;
17263  }
17264 
17265  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
17266  {
17267  Test << ASCLIt->first << '\n'; //service ref
17268  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
17269  {
17270  Test << *SCLIt << '\n';
17271  }
17272  Test << "\n\n";
17273  }
17274  Test.close();
17275  Utilities->CallLogPop();
17276  return true;
17277 */
17278 
17279  //initialise variables before calc LastTTTime & build LocServiceTimesVector
17280  if(TrainDataVector.empty())
17281  {
17282  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
17283  Utilities->CallLogPop(2209);
17284  return(false);
17285  }
17286  TLocServiceTimes TLSTEntry;
17287  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
17288  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
17289  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
17290  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
17291  LastTTTime = "";
17292  SequenceLog += "3\n";
17293  //calculate LastTTTime
17294  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17295  {
17296  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
17297  TActionVector &ActionVector = TrainDataEntry.ActionVector;
17298  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
17299  TDateTime LastTDTime;
17300  int IncMinutes = 0;
17301  NumTrains = TrainDataEntry.NumberOfTrains;
17302  if(ActionVector.empty())
17303  {
17304  continue;
17305  }
17306  if(ActionVector.at(0).SignallerControl)
17307  {
17308  continue;
17309  }
17310  if(AVLast->FormatType == Repeat)
17311  {
17312  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
17313  AVLast--; //now points to the command before the repeat
17314  }
17315  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
17316  {
17317  AVLast--; //points to last timed entry
17318  }
17319  //here AVLast points to last entry with a time
17320  if(AVLast->ArrivalTime != TDateTime(-1))
17321  {
17322  LastTDTime = AVLast->ArrivalTime;
17323  }
17324  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
17325  {
17326  LastTDTime = AVLast->EventTime;
17327  }
17328  else
17329  {
17330  continue; //shouldn't ever reach here but if do then skip this service
17331  }
17332  if(NumTrains == 1)
17333  {
17334  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
17335  }
17336  else
17337  {
17338  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
17339  }
17340  if(LastAnsiTime > LastTTTime)
17341  {
17342  LastTTTime = LastAnsiTime;
17343  }
17344  }
17345  SequenceLog += "4\n";
17346 //build LocServiceTimesVector
17347 
17348 /*
17349  struct TLocServiceTimes
17350  {
17351  AnsiString Location;
17352  AnsiString ServiceAndRepeatNum;
17353  AnsiString AtLocTime;
17354  AnsiString ArrTime;
17355  AnsiString DepTime;
17356  AnsiString FrhMarker;
17357  };
17358  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
17359 
17360 This works as follows:
17361 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
17362 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
17363 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
17364 
17365 Every action for every train is examined and times entered as follows:-
17366 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
17367 b) an unlocated Snt: entry time becomes DepTime
17368 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
17369 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
17370 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
17371 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
17372 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
17373 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
17374 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
17375 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
17376 */
17377  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17378  {
17379  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
17380  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
17381  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
17382  int IncMinutes = 0;
17383  NumTrains = TrainDataEntry.NumberOfTrains;
17384  if(ActionVector.empty())
17385  {
17386  continue;
17387  }
17388  if(ActionVector.at(0).SignallerControl)
17389  {
17390  continue;
17391  }
17392  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
17393  {
17394  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
17395  }
17396  for(int y = 0; y < NumTrains; y++) //y is the repeat number
17397  {
17398  if(NumTrains == 1)
17399  {
17400  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
17401  }
17402  else if(y == 0)
17403  {
17404  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
17405  }
17406  else
17407  {
17408  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
17409  }
17410  for(unsigned int z = 0; z < ActionVector.size(); z++)
17411  {
17412  TActionVectorEntry AVE = ActionVector.at(z);
17413  TLSTEntry.AtLocTime = "";
17414  TLSTEntry.ArrTime = "";
17415  TLSTEntry.DepTime = "";
17416  TLSTEntry.Location = "";
17417  TLSTEntry.FrhMarker = "";
17418 
17419  if(AVE.FormatType == StartNew) //Snt only
17420  {
17421  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
17422  {
17423  TLSTEntry.Location = AVE.LocationName;
17424  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
17425  LocServiceTimesVector.push_back(TLSTEntry);
17426 
17427  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
17428  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
17429  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
17430  {
17431  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
17432  {
17433  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
17434  break;
17435  }
17436  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
17437  {
17438  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
17439  break;
17440  }
17441  }
17442  if(FoundStopTime == "")
17443  {
17444  throw Exception("Failure to determine FoundStopTime for located Snt");
17445  }
17446  int WhileCount = 0;
17447  while(true)
17448  {
17449  //add minutes until reach FoundStopTime but don't add that time
17450  WhileCount++;
17451  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17452  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
17453  TLSTEntry.DepTime = "";
17454  TLSTEntry.ArrTime = "";
17455  if(IncTime >= FoundStopTime) //don't add that time
17456  {
17457  break;
17458  }
17459  LocServiceTimesVector.push_back(TLSTEntry);
17460  if(WhileCount > 2000)
17461  {
17462  throw Exception("While loop failed to break in 2000 loops for located Snt");
17463  }
17464  }
17465  }
17466  else //unlocated Snt, use the EventTime as DepTime for this vector
17467  {
17469  if(TE.ActiveTrackElementName != "")
17470  {
17471  TLSTEntry.Location = TE.ActiveTrackElementName;
17472  }
17473  else
17474  {
17475  int HLoc = TE.HLoc;
17476  int VLoc = TE.VLoc;
17477  AnsiString HString;
17478  AnsiString VString;
17479  if(HLoc < 0)
17480  {
17481  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
17482  }
17483  else
17484  {
17485  HString = AnsiString(HLoc);
17486  }
17487  if(VLoc < 0)
17488  {
17489  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
17490  }
17491  else
17492  {
17493  VString = AnsiString(VLoc);
17494  }
17495  TLSTEntry.Location = HString + '-' + VString;
17496  }
17497  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
17498  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
17499  LocServiceTimesVector.push_back(TLSTEntry);
17500  }
17501  }
17502 
17503  else if(AVE.SequenceType == Start) //other start entries, all located
17504  {
17505  TLSTEntry.Location = AVE.LocationName;
17506  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
17507  LocServiceTimesVector.push_back(TLSTEntry);
17508  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
17509  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
17510  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
17511  {
17512  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
17513  {
17514  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
17515  break;
17516  }
17517  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
17518  {
17519  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
17520  break;
17521  }
17522  }
17523  if(FoundStopTime == "")
17524  {
17525  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
17526  }
17527  int WhileCount = 0;
17528  while(true)
17529  {
17530  //add minutes until reach FoundStopTime but don't add that time
17531  WhileCount++;
17532  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17533  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
17534  TLSTEntry.DepTime = "";
17535  TLSTEntry.ArrTime = "";
17536  if(IncTime >= FoundStopTime) //don't add that time
17537  {
17538  break;
17539  }
17540  LocServiceTimesVector.push_back(TLSTEntry);
17541  if(WhileCount > 2000)
17542  {
17543  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
17544  }
17545  }
17546  }
17547 
17548  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
17549  {
17550  TLSTEntry.Location = AVE.LocationName;
17551  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
17552  {
17553  bool SkipAddingMinutes = false;
17554  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
17555  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
17556  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
17557  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
17558  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
17559  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
17560  {
17561  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
17562  {
17563  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
17564  break;
17565  }
17566  if(ActionVector.at(a).SequenceType == Finish) //finish catered for in a later test
17567  {
17568  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
17569  if((a <= (z + 2)) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
17570  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
17571  //at v2.10.0 changed (a == z + 1) to (a <= (z + 2)) as can have a cdt between, this allows for that
17572  {
17573  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
17574  SkipAddingMinutes = true;
17575  }
17576  break;
17577  }
17578  }
17579  if(FoundStopTime == "")
17580  {
17581  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
17582  }
17583  if(!SkipAddingMinutes)
17584  {
17585  int WhileCount = 0;
17586  while(true)
17587  {
17588  //add minutes until reach FoundStopTime but don't add that time
17589  WhileCount++;
17590  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17591  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
17592  TLSTEntry.DepTime = "";
17593  TLSTEntry.ArrTime = "";
17594  if(IncTime >= FoundStopTime) //don't add that time
17595  {
17596  break;
17597  }
17598  LocServiceTimesVector.push_back(TLSTEntry);
17599  if(WhileCount > 2000)
17600  {
17601  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
17602  }
17603  }
17604  }
17605  }
17606  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
17607  {
17608  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
17609  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
17610  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
17611  {
17612  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
17613  {
17614  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
17615  LocServiceTimesVector.pop_back();
17616  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
17617  }
17618  else //just add the dep & atloc times
17619  {
17620  TLSTEntry.ArrTime = "";
17621  LocServiceTimesVector.push_back(TLSTEntry);
17622  }
17623  }
17624  else //just add the dep & atloc times
17625  {
17626  TLSTEntry.ArrTime = "";
17627  LocServiceTimesVector.push_back(TLSTEntry);
17628  }
17629  }
17630  }
17631 
17632  else if(AVE.FormatType == TimeTimeLoc)
17633  {
17634  TLSTEntry.Location = AVE.LocationName;
17635  if(AVE.ArrivalTime > TDateTime(-1)) //should be
17636  {
17637  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
17638  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
17639  }
17640  if(AVE.DepartureTime > TDateTime(-1)) //should be
17641  {
17642  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
17643  }
17644  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
17645  {
17646  LocServiceTimesVector.push_back(TLSTEntry);
17647  }
17648  else
17649  {
17650  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
17651  TLSTEntry.DepTime = "";
17652  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
17653  TLSTEntry.ArrTime = ""; //done with this now
17654  while(TLSTEntry.AtLocTime < TempDepTime)
17655  {
17656  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17657  if(TLSTEntry.AtLocTime == TempDepTime)
17658  {
17659  TLSTEntry.DepTime = TempDepTime; //restore value
17660  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
17661  }
17662  else
17663  {
17664  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
17665  }
17666  }
17667  }
17668  }
17669 
17670  else if(AVE.FormatType == PassTime) //added at v2.9.1
17671  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
17672  TLSTEntry.Location = AVE.LocationName;;
17673  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
17674  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
17675  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
17676  TLSTEntry.ArrTime = ""; //need to reset this to null
17677  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
17678  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
17679  }
17680 
17681  else if(AVE.FormatType == ExitRailway) //Fer
17682  {
17683  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
17684  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
17685  //be wrong, but can't guess from here & most will have same name
17686  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
17687  if(LName != "")
17688  {
17689  TLSTEntry.Location = LName;
17690  }
17691  else
17692  {
17693  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
17694  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
17695  AnsiString HString;
17696  AnsiString VString;
17697  if(HLoc < 0)
17698  {
17699  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
17700  }
17701  else
17702  {
17703  HString = AnsiString(HLoc);
17704  }
17705  if(VLoc < 0)
17706  {
17707  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
17708  }
17709  else
17710  {
17711  VString = AnsiString(VLoc);
17712  }
17713  TLSTEntry.Location = HString + '-' + VString;
17714  }
17715  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
17716  }
17717 
17718  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
17719  {
17720  AnsiString FrhTime;
17721  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
17722  {
17723  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
17724  }
17725  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
17726  {
17727  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
17728  }
17729  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
17730  TLSTEntry.Location = AVE.LocationName;
17731  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17732  TLSTEntry.FrhMarker = "Frh";
17733  LocServiceTimesVector.push_back(TLSTEntry);
17734  TLSTEntry.FrhMarker = "";
17735  //add all times from next minute to end of timetable
17736  while(IncTime <= LastTTTime)
17737  {
17738  TLSTEntry.AtLocTime = IncTime;
17739  LocServiceTimesVector.push_back(TLSTEntry);
17740  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
17741  }
17742  }
17743 
17744  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
17745  {
17746  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
17747  {
17748  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
17749  TLSTEntry.Location = AVE.LocationName;
17750  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17751  TLSTEntry.FrhMarker = "Frh";
17752  LocServiceTimesVector.push_back(TLSTEntry);
17753  TLSTEntry.FrhMarker = "";
17754  //add all times from next minute to end of timetable
17755  while(IncTime <= LastTTTime)
17756  {
17757  TLSTEntry.AtLocTime = IncTime;
17758  LocServiceTimesVector.push_back(TLSTEntry);
17759  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
17760  }
17761  }
17762  }
17763 
17764  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
17765  {
17766  //nothing is done here as the entry will be listed at this time under the new service reference
17767  }
17768  }
17769  }
17770  }
17771  SequenceLog += "5\n";
17772  //now sort in location order
17773  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
17774  //LocServiceTimesVector now complete & sorted in location order
17775 
17776 /*
17777 //start of debugging section
17779  std::ofstream LSTVFile("LSTVFile.txt");
17780  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
17781  {
17782  LSTVFile << LSTVIt->Location + '\n';
17783  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
17784  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
17785  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
17786  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
17787  if(LSTVIt->FrhMarker == "")
17788  {
17789  LSTVFile << "Not Frh\n";
17790  }
17791  else
17792  {
17793  LSTVFile << LSTVIt->FrhMarker + '\n';
17794  }
17795  LSTVFile << '\n';
17796  }
17797  LSTVFile.close();
17798 //end of debugging section
17799 */
17800 
17801  //declare pointers for use in printouts
17802  TLocServiceTimesVector::iterator Ptr1, Ptr2;
17803 
17804  //set up the output file
17805  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17806  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
17807 
17808  std::ofstream TTFile3(TTFileName3.c_str());
17809 
17810  if(TTFile3 == 0)
17811  {
17812  ShowMessage("Conflict Analysis file failed to open - can't be created");
17813  Utilities->CallLogPop(2210);
17814  return(false);
17815  }
17816  if(LocServiceTimesVector.empty())
17817  {
17818  ShowMessage("No timetabled services found");
17819  TTFile3.close();
17820  DeleteFile(TTFileName3);
17821  Utilities->CallLogPop(2211);
17822  return(false);
17823  }
17824  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n";
17825  TTFile3 << "See user manual or on-screen help section 5.12 for detailed information.\n\n\n";
17826  SequenceLog += "6\n";
17827 
17828 /*
17829 //print out TrainDataVectorCopy for debugging purposes NB THIS USES TDateTime.TimeString, which rolls over at 24 hours, use Format96 instead
17830 std::ofstream TDVCFile("TDVCFile.txt");
17831 for(TTrainDataVector::iterator TDVCIt = TrainDataVectorCopy.begin(); TDVCIt != TrainDataVectorCopy.end(); TDVCIt++)
17832 {
17833  TDVCFile << TDVCIt->ServiceReference + '\n';
17834  TDVCFile << TDVCIt->Description + '\n';
17835  for(unsigned int x = 0; x < TDVCIt->ActionVector.size(); x++)
17836  {
17837  TActionVectorEntry AVE = TDVCIt->ActionVector.at(x);
17838  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
17839  {
17840  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
17841  }
17842  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
17843  {
17844  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << " Arr " << AVE.LocationName << '\n';
17845  }
17846  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
17847  {
17848  TDVCFile << AnsiString(AVE.DepartureTime.TimeString()) << " Dep " << AVE.LocationName << '\n';
17849  }
17850  else if(AVE.FormatType == TimeTimeLoc)
17851  {
17852  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << ' ' << AnsiString(AVE.DepartureTime.TimeString()) << ' ' << AVE.LocationName << '\n';
17853  }
17854  else if(AVE.FormatType == PassTime)
17855  {
17856  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
17857  }
17858  else if(AVE.FormatType == ExitRailway)
17859  {
17860  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << " Fer" << '\n';
17861  }
17862  else if(AVE.FormatType == FinRemHere)
17863  {
17864  TDVCFile << "Frh" << '\n';
17865  }
17866  }
17867  TDVCFile << '\n';
17868 }
17869 TDVCFile.close();
17870 */
17871 
17872  //arrivals
17873  if(ArrChecked)
17874  {
17875  //sort in ArrTime order for each location
17876  Ptr1 = LocServiceTimesVector.begin();
17877  Ptr2 = Ptr1 + 1;
17878  while(Ptr2 != LocServiceTimesVector.end())
17879  {
17880  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17881  {
17882  Ptr2++;
17883  if(Ptr2 == LocServiceTimesVector.end())
17884  {
17885  break;
17886  }
17887  }
17888  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
17889  Ptr1 = Ptr2; //first entry with next name
17890  if(Ptr2 != LocServiceTimesVector.end())
17891  {
17892  Ptr2++;
17893  }
17894  }
17895 
17896  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
17897 
17898  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
17899  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
17900  MinuteString = " minutes";
17901  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17902  if(ArrRange == 1)
17903  {
17904  MinuteString = " minute";
17905  }
17906  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
17907  TTFile3 << ",Platforms,Trains\n\n";
17908 
17909  Ptr1 = LocServiceTimesVector.begin();
17910  Ptr2 = Ptr1 + 1;
17911  while(Ptr2 != LocServiceTimesVector.end())
17912  {
17913  PreviousService = "";
17914  NumTrainsAtLoc = 0;
17915  ServiceAndRepeatNumTotal = "";
17916  NumPlats = 0;
17917  NumPlatsAtThisLocCalculated = false;
17918  BasicTime = "";
17919  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17920  {
17921  PreviousService = "";
17922  NumTrainsAtLoc = 0;
17923  ServiceAndRepeatNumTotal = "";
17924  NumPlats = 0;
17925  NumPlatsAtThisLocCalculated = false;
17926  BasicTime = "";
17927  Ptr1++;
17928  Ptr2++;
17929  if(Ptr2 == LocServiceTimesVector.end())
17930  {
17931  break;
17932  }
17933  }
17934  if(Ptr2 == LocServiceTimesVector.end())
17935  {
17936  break;
17937  }
17938  while(Ptr2->Location == Ptr1->Location)
17939  {
17940  PreviousService = "";
17941  NumTrainsAtLoc = 0;
17942  ServiceAndRepeatNumTotal = "";
17943  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
17944  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17945  {
17946  break;
17947  }
17948  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
17949  {
17950  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
17951  Ptr1++;
17952  Ptr2++;
17953  if(Ptr2 == LocServiceTimesVector.end())
17954  {
17955  break;
17956  }
17957  if(Ptr2->Location != Ptr1->Location)
17958  {
17959  break;
17960  }
17961  }
17962  if(Ptr2 == LocServiceTimesVector.end())
17963  {
17964  break;
17965  }
17966  if(Ptr2->Location != Ptr1->Location)
17967  {
17968  break;
17969  }
17970  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
17971  {
17972  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
17973  {
17974  break;
17975  }
17976  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17977  {
17978  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
17979  NumPlatsAtThisLocCalculated = true;
17980  }
17981  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17982  {
17983  if(ServiceAndRepeatNumTotal == "")
17984  {
17985  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
17986  NumTrainsAtLoc = 1;
17987  }
17988  else
17989  {
17990  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
17991  }
17992  }
17993  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
17994  if(ServiceAndRepeatNumTotal == "")
17995  {
17996  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
17997  NumTrainsAtLoc = 1;
17998  }
17999  else
18000  {
18001  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
18002  }
18003  Ptr1 = Ptr2;
18004  Ptr2++;
18005  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
18006  {
18007  int MaxNumberOfSameDirections = 0;
18008  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
18009  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
18010  {
18011 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
18012  TTFile3.close();
18013  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
18014 // Utilities->CallLogPop(2224);
18015 // return false;
18016  }
18017  AnsiString Asterisk = "";
18018  if(MaxNumberOfSameDirections >= NumPlats)
18019  {
18020  Asterisk = "* ";
18021  }
18022  //print out a single line for number of trains at loc with all service refs
18023  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
18024  ArrivalsPrinted = true;
18025  ServiceAndRepeatNumTotal = "";
18026  }
18027  if(Ptr2 == LocServiceTimesVector.end())
18028  {
18029  break;
18030  }
18031  if(Ptr2->Location != Ptr1->Location)
18032  {
18033  break;
18034  }
18035  }
18036  if(Ptr2 == LocServiceTimesVector.end())
18037  {
18038  break;
18039  }
18040  }
18041  }
18042  if(!ArrivalsPrinted)
18043  {
18044  TTFile3 << "Nothing to report for arrivals";
18045  }
18046  TTFile3 << "\n\n";
18047  }
18048  //end of routine for arrivals
18049  SequenceLog += "7\n";
18050  //departures
18051  if(DepChecked)
18052  {
18053  //sort in DepTime order for each location
18054  Ptr1 = LocServiceTimesVector.begin();
18055  Ptr2 = Ptr1 + 1;
18056  while(Ptr2 != LocServiceTimesVector.end())
18057  {
18058  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
18059  {
18060  Ptr2++;
18061  if(Ptr2 == LocServiceTimesVector.end())
18062  {
18063  break;
18064  }
18065  }
18066  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
18067  Ptr1 = Ptr2; //first entry with next name
18068  if(Ptr2 != LocServiceTimesVector.end())
18069  {
18070  Ptr2++;
18071  }
18072  }
18073 
18074  //routine for departures - number of trains departing within the specified range with services listed at the end
18075  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
18076  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
18077  MinuteString = " minutes";
18078  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
18079  if(DepRange == 1)
18080  {
18081  MinuteString = " minute";
18082  }
18083  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
18084  TTFile3 << ",Platforms,Trains\n\n";
18085 
18086  Ptr1 = LocServiceTimesVector.begin();
18087  Ptr2 = Ptr1 + 1;
18088  while(Ptr2 != LocServiceTimesVector.end())
18089  {
18090  PreviousService = "";
18091  NumTrainsAtLoc = 0;
18092  ServiceAndRepeatNumTotal = "";
18093  NumPlats = 0;
18094  NumPlatsAtThisLocCalculated = false;
18095  BasicTime = "";
18096  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
18097  {
18098  PreviousService = "";
18099  NumTrainsAtLoc = 0;
18100  ServiceAndRepeatNumTotal = "";
18101  NumPlats = 0;
18102  NumPlatsAtThisLocCalculated = false;
18103  BasicTime = "";
18104  Ptr1++;
18105  Ptr2++;
18106  if(Ptr2 == LocServiceTimesVector.end())
18107  {
18108  break;
18109  }
18110  }
18111  if(Ptr2 == LocServiceTimesVector.end())
18112  {
18113  break;
18114  }
18115  while(Ptr2->Location == Ptr1->Location)
18116  {
18117  PreviousService = "";
18118  NumTrainsAtLoc = 0;
18119  ServiceAndRepeatNumTotal = "";
18120  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
18121  if((Ptr1->Location == "") && (Ptr2->Location == ""))
18122  {
18123  break;
18124  }
18125  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
18126  {
18127  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
18128  Ptr1++;
18129  Ptr2++;
18130  if(Ptr2 == LocServiceTimesVector.end())
18131  {
18132  break;
18133  }
18134  if(Ptr2->Location != Ptr1->Location)
18135  {
18136  break;
18137  }
18138  }
18139  if(Ptr2 == LocServiceTimesVector.end())
18140  {
18141  break;
18142  }
18143  if(Ptr2->Location != Ptr1->Location)
18144  {
18145  break;
18146  }
18147  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
18148  {
18149  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
18150  {
18151  break;
18152  }
18153  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
18154  {
18155  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
18156  NumPlatsAtThisLocCalculated = true;
18157  }
18158  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
18159  {
18160  if(ServiceAndRepeatNumTotal == "")
18161  {
18162  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
18163  NumTrainsAtLoc = 1;
18164  }
18165  else
18166  {
18167  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
18168  }
18169  }
18170  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
18171  if(ServiceAndRepeatNumTotal == "")
18172  {
18173  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
18174  NumTrainsAtLoc = 1;
18175  }
18176  else
18177  {
18178  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
18179  }
18180  Ptr1 = Ptr2;
18181  Ptr2++;
18182  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
18183  {
18184  int MaxNumberOfSameDirections = 0;
18185  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
18186  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
18187  {
18188 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
18189  TTFile3.close();
18190  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
18191 // Utilities->CallLogPop(2225);
18192 // return false;
18193  }
18194  AnsiString Asterisk = "";
18195  if(MaxNumberOfSameDirections >= NumPlats)
18196  {
18197  Asterisk = "* ";
18198  }
18199  //print out a single line for number of trains at loc with all service refs
18200  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
18201  DeparturesPrinted = true;
18202  ServiceAndRepeatNumTotal = "";
18203  }
18204  if(Ptr2 == LocServiceTimesVector.end())
18205  {
18206  break;
18207  }
18208  if(Ptr2->Location != Ptr1->Location)
18209  {
18210  break;
18211  }
18212  }
18213  if(Ptr2 == LocServiceTimesVector.end())
18214  {
18215  break;
18216  }
18217  }
18218  }
18219  if(!DeparturesPrinted)
18220  {
18221  TTFile3 << "Nothing to report for departures";
18222  }
18223  TTFile3 << "\n\n";
18224  }
18225  //end of routine for departures
18226  SequenceLog += "8\n";
18227 
18228  //list trains at locations at same time
18229 
18230  if(AtLocChecked)
18231  {
18232  //sort in AtLocTime order for each location
18233  Ptr1 = LocServiceTimesVector.begin();
18234  Ptr2 = Ptr1 + 1;
18235  while(Ptr2 != LocServiceTimesVector.end())
18236  {
18237  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
18238  {
18239  Ptr2++;
18240  if(Ptr2 == LocServiceTimesVector.end())
18241  {
18242  break;
18243  }
18244  }
18245  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
18246  Ptr1 = Ptr2; //first entry with next name
18247  if(Ptr2 != LocServiceTimesVector.end())
18248  {
18249  Ptr2++;
18250  }
18251  }
18252 
18253  //print out simultaneous AtLocs (don't need range of times for AtLocs)
18254  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
18255  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
18256  TTFile3 << ",Platforms,Trains,\n\n";
18257  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
18258  Ptr1 = LocServiceTimesVector.begin();
18259  Ptr2 = Ptr1 + 1;
18260  while(Ptr2 != LocServiceTimesVector.end())
18261  {
18262  PreviousService = "";
18263  ServiceAndRepeatNumTotal = "";
18264  NumTrainsAtLoc = 0;
18265  NumPlats = 0;
18266  NumPlatsAtThisLocCalculated = false;
18267  FrhCount = 0;
18268 
18269  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
18270  {
18271  PreviousService = "";
18272  ServiceAndRepeatNumTotal = "";
18273  NumTrainsAtLoc = 0;
18274  NumPlats = 0;
18275  NumPlatsAtThisLocCalculated = false;
18276  FrhCount = 0;
18277  Ptr1++;
18278  Ptr2++;
18279  if(Ptr2 == LocServiceTimesVector.end())
18280  {
18281  break;
18282  }
18283  }
18284  if(Ptr2 == LocServiceTimesVector.end())
18285  {
18286  break;
18287  }
18288  while(Ptr2->Location == Ptr1->Location)
18289  {
18290  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
18291  {
18292  FrhCount++;
18293  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
18294  }
18295  PreviousService = "";
18296  NumTrainsAtLoc = 0;
18297  ServiceAndRepeatNumTotal = "";
18298  if((Ptr1->Location == "") && (Ptr2->Location == ""))
18299  {
18300  break;
18301  }
18302  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
18303  {
18304  Ptr1++;
18305  if(Ptr1->FrhMarker == "Frh")
18306  {
18307  FrhCount++;
18308  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
18309  }
18310  Ptr2++;
18311  if(Ptr2 == LocServiceTimesVector.end())
18312  {
18313  break;
18314  }
18315  if(Ptr2->Location != Ptr1->Location)
18316  {
18317  break;
18318  }
18319  }
18320  if(Ptr2 == LocServiceTimesVector.end())
18321  {
18322  break;
18323  }
18324  if(Ptr2->Location != Ptr1->Location)
18325  {
18326  break;
18327  }
18328  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
18329  {
18330  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
18331  {
18332  break;
18333  }
18334  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
18335  {
18336  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
18337  NumPlatsAtThisLocCalculated = true;
18338  }
18339  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
18340  {
18341  if(ServiceAndRepeatNumTotal == "")
18342  {
18343  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
18344  NumTrainsAtLoc = 1;
18345  }
18346  else
18347  {
18348  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
18349  }
18350  }
18351  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
18352  if(ServiceAndRepeatNumTotal == "")
18353  {
18354  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
18355  NumTrainsAtLoc = 1;
18356  }
18357  else
18358  {
18359  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
18360  }
18361  Ptr1 = Ptr2;
18362  if(Ptr1->FrhMarker == "Frh")
18363  {
18364  FrhCount++;
18365  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
18366  }
18367  Ptr2++;
18368  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
18369  {
18370 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
18371 //new text //don't print out if all remainers or if only 1 train at loc
18372  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
18373 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
18374 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
18375  {
18376  AnsiString Asterisk = "";
18377  if(NumTrainsAtLoc > NumPlats)
18378  {
18379  Asterisk = "* ";
18380  }
18381  //print out a single line for number of trains at loc with all service refs
18382  if(FrhCount == 0)
18383  {
18384  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
18385  }
18386  else if(FrhCount == 1)
18387  {
18388  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
18389  }
18390  else
18391  {
18392  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
18393  }
18394  LastFrhCount = FrhCount;
18395  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
18396  AtLocsPrinted = true;
18397  ServiceAndRepeatNumTotal = "";
18398  }
18399  }
18400  if(Ptr2 == LocServiceTimesVector.end())
18401  {
18402  break;
18403  }
18404  if(Ptr2->Location != Ptr1->Location)
18405  {
18406  break;
18407  }
18408  }
18409  if(Ptr2 == LocServiceTimesVector.end())
18410  {
18411  break;
18412  }
18413  }
18414  }
18415  if(!AtLocsPrinted)
18416  {
18417  TTFile3 << "Nothing to report for trains at locations";
18418  }
18419  TTFile3 << "\n\n";
18420  //end of simultaneous AtLocs
18421  }
18422  SequenceLog += "9\n";
18423 /*
18424  //print out the full vector here for testing purposes
18425  TTFile3 << "Full LocServiceTimesVector\n\n";
18426  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
18427 
18428  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
18429  {
18430  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
18431  }
18432 
18433  TTFile3 << "\n\n\n";
18434 */
18435 /*cdt analysis - added at v2.10.0
18436 2 pass system: 1st extract as a single service all Snt (or Snt-sh) starts, with Fns/Sns links combined (and F-nshs/Sns-sh) (though add a new
18437 changeover code [chr XXXX - 'change ref + new reference] until come to Fjo, Frh, Frh-sh, Fer (ignore exit loc as can't stop there), ignore jbos &
18438 repeats, but with fsp & rsp store all the foregoing service entries along with the split reference & add that to the relevant Sfs entry as a new
18439 service. For shuttles with feeder start with feeder & progress into shuttle, ending when finish & remain here or progressing into the finishing
18440 service.
18441 
18442 Use The TrainDataVectorCopy as that has all unique service refs.
18443 
18444 2nd run the cdt checker similar to that in SecondPassActions, but where a same name found either side of a changeover code quote both refs. Add a
18445 similar unexpected cdt check where if have different locs either side of a cdt then may be inappropriate.
18446 
18447 First create a new TrainDataVector from earlier copy as above with single services
18448 */
18449  if(DirChecked)
18450  {
18451  //direction analysis added at v2.10.0
18452  TTrainDataEntry SingleServiceEntry, PartServiceEntry, NewPartServiceEntry, TempEntry;
18453  TTrainDataVector SingleServiceVector, PartServiceVector;
18454 
18455  //find new train services (Snt or Snt-sh) & remember that entries can be in any order
18456  //NB: ALWAYS use OtherHeadCode (which is now a service reference) for any follow-on service
18457  TTFile3 << "Train direction analysis - consisting of train facing directions on creation and possible missing or questionable changes of direction:\n\n";
18458  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
18459  {
18460  TTrainDataEntry TDE = TrainDataVectorCopy.at(x);
18461  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
18462  {
18463  TDE.ActionVector.erase(&TDE.ActionVector.back()); //strip repeat entry if present
18464  }
18465  const TActionVector &AV = TDE.ActionVector;
18466  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
18467  {
18468  SingleServiceEntry = TDE;
18469  TActionVector &SSAV = SingleServiceEntry.ActionVector;
18470  for(unsigned int y = 0; y < SSAV.size(); y++)
18471  {
18472  if((SSAV.at(y).Command == "Fjo") || (SSAV.at(y).Command == "Frh") || (SSAV.at(y).Command == "Fer") || (SSAV.at(y).Command == "Frh-sh"))
18473  {
18474  SingleServiceVector.push_back(SingleServiceEntry); //push the complete entry
18475  break; //finished with this one
18476  }
18477  else if((SSAV.at(y).Command == "fsp") || (SSAV.at(y).Command == "rsp"))
18478  {
18479  PartServiceEntry = TDE; //start with complete entry
18480  PartServiceEntry.ActionVector.clear(); //clear AV
18481  for(unsigned int z = 0; z <= y; z++)
18482  {
18483  PartServiceEntry.ActionVector.push_back(TDE.ActionVector.at(z)); //add back all AVs up to & inc fsp/rsp
18484  if(z == y)
18485  {
18486  PartServiceEntry.ActionVector.at(z).Command = "chr-sp"; //change split command to chr
18487  PartServiceEntry.ActionVector.at(z).OtherHeadCode = PartServiceEntry.ActionVector.at(z).LinkedTrainEntryPtr->ServiceReference;
18488  }
18489  }
18490  PartServiceVector.push_back(PartServiceEntry);
18491  if(SSAV.at(y).Command == "fsp")
18492  {
18493  SSAV.at(y).Command = "Front split - original service continues below";
18494  SSAV.at(y).OtherHeadCode = "";
18495  }
18496  if(SSAV.at(y).Command == "rsp")
18497  {
18498  SSAV.at(y).Command = "Rear split - original service continues below";
18499  SSAV.at(y).OtherHeadCode = "";
18500  }
18501  //don't break & continue here because the original train carries on
18502  }
18503  else if(SSAV.at(y).Command == "Fns")
18504  {
18505  SSAV.at(y).Command = "chr-Fns";
18506  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
18507  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
18508  break; //from y loop
18509  }
18510  else if(SSAV.at(y).Command == "Fns-sh")
18511  {
18512  SSAV.at(y).Command = "chr-Fns-sh";
18513  SSAV.at(y).OtherHeadCode = SSAV.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18514  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
18515  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
18516  break; //from y loop
18517  }
18518  else if(SSAV.at(y).Command == "F-nshs")
18519  {
18520  SSAV.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
18521  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
18522  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
18523  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
18524  break; //from y loop
18525  }
18526  }
18527  }
18528  }
18529  SequenceLog += "10\n";
18530  //now have all complete entries in SingleServiceVector and all part services in PartServiceVector but without any follow-ons
18531 
18532  //Now add Sns & Sns-sh services to PartServiceVector entries
18533  AnsiString NextRef;
18534  while(!PartServiceVector.empty())
18535  {
18536  PartServiceEntry = PartServiceVector.at(0); //deal with front entry and add new entries at the back
18537  for(unsigned int y = 0; y < PartServiceEntry.ActionVector.size(); y++)
18538  {
18539  if(PartServiceEntry.ActionVector.at(y).Command.SubString(1,3) == "chr")
18540  {
18541  NextRef = PartServiceEntry.ActionVector.at(y).OtherHeadCode;
18542  }
18543  }
18544  //find it in TrainDataVectorCopy
18545  bool FinishType = true, FoundFlag = false;
18546  while(FinishType)
18547  {
18548  TempEntry = GetServiceFromVector(0, NextRef, TrainDataVectorCopy, FinishType, FoundFlag); //FinishType is a bool where false = Final (Fjo, Frh, Fer, or
18549  //Frh-sh); true = MoreToCome (Fns, Fns-sh, F-nshs)
18550  if(FoundFlag)
18551  {
18552  for(unsigned int y = 1; y < TempEntry.ActionVector.size(); y++) //starts at 1 as that is the entry after the start entry
18553  {
18554  if((TempEntry.ActionVector.at(y).Command == "") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
18555  {
18556  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
18557  }
18558  else if((TempEntry.ActionVector.at(y).Command[1] != 'F') && (TempEntry.ActionVector.at(y).Command != "fsp") && (TempEntry.ActionVector.at(y).Command != "rsp") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
18559  {
18560  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
18561  }
18562  else
18563  {
18564  if((TempEntry.ActionVector.at(y).Command == "Fjo") || (TempEntry.ActionVector.at(y).Command == "Frh") || (TempEntry.ActionVector.at(y).Command == "Fer") || (TempEntry.ActionVector.at(y).Command == "Frh-sh"))
18565  {
18566  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
18567  SingleServiceVector.push_back(PartServiceVector.at(0)); //push the complete entry
18568  PartServiceVector.erase(&PartServiceVector.at(0));
18569  break; //from y loop
18570  }
18571  else if((TempEntry.ActionVector.at(y).Command == "fsp") || (TempEntry.ActionVector.at(y).Command == "rsp"))
18572  {
18573  NewPartServiceEntry = PartServiceVector.at(0); //covers everything up to but excluding the split
18574  NewPartServiceEntry.ActionVector.push_back(TempEntry.ActionVector.at(y)); //now includes the split
18575  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).Command = "chr-sp"; //change split command to chr
18576  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).OtherHeadCode = NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).LinkedTrainEntryPtr->ServiceReference;
18577  PartServiceVector.push_back(NewPartServiceEntry); //new entry for the split service
18578  if(TempEntry.ActionVector.at(y).Command == "fsp")
18579  {
18580  TempEntry.ActionVector.at(y).Command = "Front split - original service continues below";
18581  TempEntry.ActionVector.at(y).OtherHeadCode = "";
18582  }
18583  if(TempEntry.ActionVector.at(y).Command == "rsp")
18584  {
18585  TempEntry.ActionVector.at(y).Command = "Rear split - original service continues below";
18586  TempEntry.ActionVector.at(y).OtherHeadCode = "";
18587  }
18588  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
18589  }
18590  else if(TempEntry.ActionVector.at(y).Command == "Fns")
18591  {
18592  TempEntry.ActionVector.at(y).Command = "chr-Fns";
18593  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
18594  TempEntry.ActionVector.at(y).OtherHeadCode = NextRef;
18595  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
18596  break; //from y loop
18597  }
18598  else if(TempEntry.ActionVector.at(y).Command == "Fns-sh")
18599  {
18600  TempEntry.ActionVector.at(y).Command = "chr-Fns-sh";
18601  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18602  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
18603  NextRef = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18604  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
18605  break; //from y loop
18606  }
18607  else if(TempEntry.ActionVector.at(y).Command == "F-nshs")
18608  {
18609  TempEntry.ActionVector.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
18610  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
18611  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
18612  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
18613  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
18614  break; //from y loop
18615  }
18616  }
18617  }
18618  }
18619  else
18620  {
18621  SequenceLog += + "11\n";
18622  throw Exception("Unable to find service reference " + NextRef + " Last ref checked = " + TempEntry.ServiceReference);
18623  }
18624  }
18625  }
18626  if(!PartServiceVector.empty())
18627  {
18628  SequenceLog += "12\n";
18629  throw Exception("PartServiceVector should be empty here - size = " + PartServiceVector.size());
18630  }
18631  SequenceLog += "13\n";
18632  /*
18633  form:-
18634  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
18635  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
18636  then multiple entries, separated by commas, of the form:-
18637 
18638  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
18639  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
18640  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
18641 
18642  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
18643  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
18644  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
18645 
18646  HH:MM;Command (cdt) }TimeCmd }
18647  HH:MM;Location (arr & dep) }TimeLoc }
18648  HH:MM;HH:MM;Location }TimeTimeLoc }
18649  HH:MM;pas;Location }PassTime }
18650  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
18651  HH:MM;Fer;set of allowable IDs }ExitRailway }
18652  Command (Frh only) }FinRemHere }
18653 
18654  R;mm;dd;nn. Repeat Repeat entry
18655 
18656  Formats:
18657 
18658  Command only: Frh
18659  Time;Command: cdt
18660  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
18661  Time;Command;2 Element IDs: Snt
18662  Time;Comand;n Element IDs: Fer
18663  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
18664  Time;Command;2 Element IDs;Headcode Snt-sh
18665  Time;Command;Location pas
18666  Time;Location Arr Dep
18667  Time;Time;Location Arr & dep together
18668  */
18669 
18670 /*
18671 Perform the starting direction check (Snt & Snt-sh entries). Starts with the train's front element,
18672 checking forwards until it comes to a continuation (no report), a location name that is not null and
18673 different to the train's front element name (whether null or not) (no report), a leading point
18674 (no report) or buffers (report).
18675 */
18676  bool BufferFacingUnReportedFlag = true;
18677  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18678  {
18679  TTrackElement ThisElement, NextElement;
18680  TTrainDataEntry TDE = SingleServiceVector.at(x);
18681  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
18682  {
18683  SequenceLog += "13a\n";
18684  throw Exception("Repeat entry present in SingleServiceVector at position " + x);
18685  }
18686  const TActionVector &AV = TDE.ActionVector;
18687  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
18688  {
18689  bool BufferFlag = false;
18690  int FrontTVPos = AV.at(0).FrontStartOrRepeatDigits;
18691  int RearTVPos = AV.at(0).RearStartOrRepeatMins;
18692  AnsiString FrontLocName = AV.at(0).LocationName;
18693  int NextEntryPos, NextExitPos;
18694  ThisElement = Track->TrackElementAt(1395, FrontTVPos);
18695  int ThisExitPos;
18696  if(ThisElement.Conn[0] == RearTVPos)
18697  {
18698  ThisExitPos = 1;
18699  }
18700  else if(ThisElement.Conn[1] == RearTVPos)
18701  {
18702  ThisExitPos = 0;
18703  }
18704  else if(ThisElement.Conn[2] == RearTVPos)
18705  {
18706  ThisExitPos = 3;
18707  }
18708  else if(ThisElement.Conn[3] == RearTVPos)
18709  {
18710  ThisExitPos = 2;
18711  }
18712  if((ThisElement.TrackType == Buffers) && (ThisExitPos == 0))//pos 0 is the buffer
18713  {
18714  BufferFlag = true;
18715  }
18716  else //continue tracking forwards
18717  {
18718  while(true)
18719  {
18720  if(ThisElement.Conn[ThisExitPos] == -1)
18721  {
18722  SequenceLog = "13b\n";
18723  throw Exception("ThisElement connects to -1 for " + TDE.ServiceReference);
18724  }
18725  NextElement = Track->TrackElementAt(1396, ThisElement.Conn[ThisExitPos]);
18726  NextEntryPos = ThisElement.ConnLinkPos[ThisExitPos];
18727  if((NextElement.TrackType == Points) && ((NextEntryPos == 0) || (NextEntryPos == 2))) //leading points
18728  {
18729  BufferFlag = false; //should already be false
18730  break;
18731  }
18732  else if(NextElement.TrackType == Continuation)
18733  {
18734  BufferFlag = false;
18735  break;
18736  }
18737  else if((NextElement.ActiveTrackElementName != "") && (NextElement.ActiveTrackElementName != FrontLocName))
18738  {
18739  BufferFlag = false;
18740  break;
18741  }
18742  else if(NextElement.TrackType == Buffers)
18743  {
18744  BufferFlag = true;
18745  break;
18746  }
18747  else if((NextElement.TrackType == Points) && ((NextEntryPos == 1) || (NextEntryPos == 3))) //trailing points
18748  {
18749  ThisElement = NextElement;
18750  ThisExitPos = 0;
18751  continue;
18752  }
18753  else
18754  {
18755  if(NextEntryPos == 0)
18756  {
18757  NextExitPos = 1;
18758  }
18759  else if(NextEntryPos == 1)
18760  {
18761  NextExitPos = 0;
18762  }
18763  else if(NextEntryPos == 2)
18764  {
18765  NextExitPos = 3;
18766  }
18767  else if(NextEntryPos == 3)
18768  {
18769  NextExitPos = 2;
18770  }
18771  }
18772  ThisElement = NextElement;
18773  ThisExitPos = NextExitPos;
18774  }
18775  }
18776  if(BufferFlag)
18777  {
18778  if(BufferFacingUnReportedFlag)
18779  {
18780  TTFile3 << "Train facing direction on creation analysis:-\n\n";
18781  BufferFacingUnReportedFlag = false;
18782  }
18783  TTFile3 << "Service " + TDE.ServiceReference + " facing buffers on creation\n";
18784  }
18785  }
18786  }
18787  if(BufferFacingUnReportedFlag)
18788  {
18789  TTFile3 << "Nothing to report for train facing directions\n\n";
18790  }
18791  else
18792  {
18793  TTFile3 << '\n';
18794  }
18795  SequenceLog += "13c\n";
18796 
18797  //Perform the missing cdt check. Check every entry simiar to the check in SecondPassActions and if find any print out the full sequence and service entries
18798  AnsiString LocationNameToBeChecked = "";
18799  bool MissingcdtUnreportedFlag = true;
18800  TNumList MarkerList;
18801  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18802  {
18803  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
18804  unsigned int y = 0;
18805  int FirstInstance = 9999, SecondInstance = 9999; //9999 ensures wont be marked if not changed
18806  bool FullBreak = false;
18807  MarkerList.clear();
18808  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
18809  // first discard unlocated Snt entries as they don't have location name set
18810  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
18811  {
18812  y = 1;
18813  }
18814  while((y < TDEntry.ActionVector.size()) && !FullBreak)
18815  // need to check each location name separately in turn, skipped for SignallerControl entries
18816  {
18817  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat) ||
18818  (TDEntry.ActionVector.at(y).Command == "Fjo") || (TDEntry.ActionVector.at(y).Command == "Frh") ||
18819  (TDEntry.ActionVector.at(y).Command == "Frh-sh"))
18820  {
18821  break; // out of the 'while' loop since have reached the end
18822  }
18823  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
18824  FirstInstance = y;
18825  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
18826  {
18827  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
18828  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) ||
18829  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
18830  (AVEntry.Command == "Frh-sh"))
18831  {
18832  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
18833  }
18834  if(AVEntry.Command == "cdt")
18835  {
18836  break; // out of the 'z' loop since the check is only valid up to a change of direction
18837  }
18838  if(AVEntry.LocationName == LocationNameToBeChecked)
18839  {
18840  continue; // keep going while name same
18841  }
18842  if(AVEntry.LocationName != LocationNameToBeChecked)
18843  // if name different check forwards to see if repeats
18844  {
18845  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
18846  {
18847  if(TDEntry.ActionVector.at(a).Command == "cdt")
18848  {
18849  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
18850  }
18851  if(TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked)
18852  {
18853  SecondInstance = a;
18854  AnsiString Sequence = TDEntry.ServiceReference;
18855  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
18856  {
18857  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
18858  {
18859  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
18860  }
18861  }
18862  if(MissingcdtUnreportedFlag)
18863  {
18864  TTFile3 << "Possibly missing changes of direction - these will be missing unless the service travels in a loop back to the locations marked:-\n\n";
18865  }
18866  TTFile3 << LocationNameToBeChecked << " is listed twice with no direction change between in service sequence: " << Sequence << "\n\n";
18867  MarkerList.push_back(FirstInstance);
18868  MarkerList.push_back(SecondInstance);
18869  SingleServiceOutput(0, x, MarkerList, SingleServiceVector, TTFile3);
18870  MissingcdtUnreportedFlag = false;
18871  FullBreak = true; //no more checks for this sequence
18872  break; //out of the a & z loops
18873  }
18874  }
18875  break; // out of the 'z' loop since have checked 'a' as far as need to
18876  }
18877  }
18878  y++;
18879  }
18880  }
18881  if(MissingcdtUnreportedFlag)
18882  {
18883  TTFile3 << "Nothing to report for missing changes of direction\n\n";
18884  }
18885  else
18886  {
18887  TTFile3 << '\n';
18888  }
18889  SequenceLog += "14\n";
18890 
18891 /* Perform the questionable cdt check. Examine each service in the SingleServiceVector, and if don't find the same
18892  name either side of a cdt (before the next cdt) then flag as a questionable cdt.
18893  Method: Have an outer loop for each service that looks for cdts. When found work backwards to the last cdt or beginning and std::list all the
18894  locations excluding the cdt location. Then work forwards to the next cdt or the end and do the same. Sort each list and make unique (duplicated
18895  names on one side of a cdt already checked either by the tt validator or the missing cdt check. Then compare the two lists and if any location
18896  included in both then ok, else report as questionable. If one list is empty then it is reported.
18897 */
18898  typedef std::list<AnsiString> TLocList;
18899  TLocList BackwardList, ForwardList;
18900  bool IntroLineNeeded = true;
18901  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18902  {
18903  unsigned int cdtPosition = 9999;
18904  AnsiString cdtLocation = "";
18905  bool FoundSameName = false;
18906  MarkerList.clear();
18907  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
18908  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
18909  // need to check each location name separately in turn, skipped for SignallerControl entries
18910  {
18911  BackwardList.clear();
18912  ForwardList.clear();
18913  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
18914  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) || //end of SSVector, shouldn't be any repeats
18915  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
18916  (AVEntry.Command == "Frh-sh"))
18917  {
18918  if(MarkerList.empty())
18919  {
18920  break; // out of the 'y' loop since have reached the end & nothing to report
18921  }
18922  else
18923  {
18924  AnsiString Sequence = TDEntry.ServiceReference;
18925  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
18926  {
18927  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
18928  {
18929  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
18930  }
18931  }
18932  MarkerList.sort();
18933  if(IntroLineNeeded)
18934  {
18935  TTFile3 << "Questionable change of direction analysis.\n\n";
18936  TTFile3 << "For marked changes of direction there are no same-name locations listed both above (up to the start or another direction change)\n";
18937  TTFile3 << "and below (down to the end or another direction change) but not counting the change of direction location itself.\n\n";
18938  TTFile3 << "These changes of direction are probably valid for movements to and from depots but all should be checked to\n";
18939  TTFile3 << "make sure that none has been included incorrectly:\n\n";
18940  IntroLineNeeded = false;
18941  }
18942  TTFile3 << "Service sequence " << Sequence << " contains questionable changes of direction:-\n\n";
18943  SingleServiceOutput(1, x, MarkerList, SingleServiceVector, TTFile3);
18944  break;
18945  }
18946  }
18947  if(AVEntry.Command != "cdt")
18948  {
18949  continue; //only looking for cdts
18950  }
18951  //here have found a cdt
18952  cdtPosition = y;
18953  cdtLocation = AVEntry.LocationName;
18954  for(int z = y - 1; z >= 0; z--)
18955  {
18956  const TActionVectorEntry &AVEntry2 = TDEntry.ActionVector.at(z);
18957  if(AVEntry2.Command == "cdt")
18958  {
18959  break; //don't look further back than the last cdt
18960  }
18961  if((AVEntry2.LocationName != "") && (AVEntry2.LocationName != cdtLocation)) //if an earlier entry == cdtLocation will have been picked up in an earlier check
18962  {
18963  BackwardList.push_back(AVEntry2.LocationName);
18964  }
18965  }
18966  BackwardList.sort();
18967  BackwardList.unique();
18968  for(unsigned int z = y + 1; z < TDEntry.ActionVector.size(); z++)
18969  {
18970  const TActionVectorEntry &AVEntry3 = TDEntry.ActionVector.at(z);
18971  if((AVEntry3.Command == "Fer") || (AVEntry3.FormatType == Repeat) ||
18972  (AVEntry3.Command == "Fjo") || (AVEntry3.Command == "Frh") ||
18973  (AVEntry3.Command == "Frh-sh") || (AVEntry3.Command == "cdt"))
18974  {
18975  break; // out of the 'z' loop since have reached another cdt or the end
18976  }
18977  if((AVEntry3.LocationName != "") && (AVEntry3.LocationName != cdtLocation)) //if a later entry == cdtLocation will have been picked up in an earlier check
18978  {
18979  ForwardList.push_back(AVEntry3.LocationName);
18980  }
18981  }
18982  ForwardList.sort();
18983  ForwardList.unique();
18984  FoundSameName = false;
18985  //now have both lists compiled (may be empty) so check for same name in both & report if don't find any
18986  if(!BackwardList.empty() && !ForwardList.empty())
18987  {
18988  for(TLocList::iterator BLIt = BackwardList.begin(); BLIt != BackwardList.end(); BLIt++)
18989  {
18990  for(TLocList::iterator FLIt = ForwardList.begin(); FLIt != ForwardList.end(); FLIt++)
18991  {
18992  if(*BLIt == *FLIt)
18993  {
18994  FoundSameName = true;
18995  }
18996  }
18997  }
18998  }
18999  if(!FoundSameName) //report the inability to find same name
19000  {
19001  MarkerList.push_back(cdtPosition);
19002  }
19003  }
19004  }
19005  if(IntroLineNeeded)
19006  {
19007  TTFile3 << "Nothing to report for questionable changes of direction\n\n";
19008  }
19009  else
19010  {
19011  TTFile3 << '\n';
19012  }
19013 /*
19014 //print all SSVector for diagnostic purposes
19015  TTFile3 << "Whole SSVector\n\n";
19016  TNumList EmptyList;
19017  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
19018  {
19019  SingleServiceOutput(, x, EmptyList, SingleServiceVector, TTFile3);
19020  }
19021 */
19022  }
19023  SequenceLog += "15\n";
19024  TTFile3.close();
19025  Utilities->CallLogPop(2212);
19026  return(true);
19027  }
19028 
19029  catch(const Exception &e) //non error catch
19030  {
19031  AnsiString TTErrorFileName = "Analysis Error.txt";
19032  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
19033  std::ofstream TTError(TTErrorFileName.c_str());
19034  if(TTError == 0)
19035  {
19036  ShowMessage("Analysis error file failed to open - can't be created");
19037  Utilities->CallLogPop(2233);
19038  return(false);
19039  }
19040  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
19041  TTError << TimeNow.c_str() << '\n' << ArrRange << '\n' << ArrChecked << '\n' << DepRange << '\n' <<
19042  DepChecked << '\n' << AtLocChecked << '\n' << SequenceLog << '\n' << AnsiString(e.Message);
19043 
19044  TTError.close();
19045  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
19046  Utilities->CallLogPop(2226);
19047  return(false);
19048  }
19049 }
19050 
19051 // ---------------------------------------------------------------------------
19052 void TTrainController::SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
19053 {
19054  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(SSVectorNumber) + ',' + ",SingleServiceOutput");
19055  if((SSVectorNumber < 0) || ((unsigned int)SSVectorNumber >= SingleServiceVector.size()))
19056  {
19057  throw Exception("SSVectorNumber out of range, = " + AnsiString(SSVectorNumber) + ", Vector size = " + SingleServiceVector.size());
19058  }
19059  TTrainDataEntry SingleService = SingleServiceVector.at(SSVectorNumber);
19060  {
19061  VecFile << ",Initial service reference " << SingleService.ServiceReference + '\n';
19062  AnsiString Marker = "";
19063  for(unsigned int x = 0; x < SingleService.ActionVector.size(); x++)
19064  {
19065  Marker = ',';
19066  for(TNumListIterator MLIt = MarkerList.begin(); MLIt != MarkerList.end(); MLIt++)
19067  {
19068  if(int(x) == *MLIt)
19069  {
19070  Marker = "-->,";
19071  break;
19072  }
19073  }
19074  TActionVectorEntry AVE = SingleService.ActionVector.at(x);
19075  if(AVE.FormatType == StartNew)
19076  {
19077  AnsiString RearID = Track->TrackElementAt(1397, AVE.RearStartOrRepeatMins).ElementID;
19078  AnsiString FrontID = Track->TrackElementAt(1398, AVE.FrontStartOrRepeatDigits).ElementID;
19079  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << '\n';
19080  }
19081  if(AVE.FormatType == SNTShuttle)
19082  {
19083  AnsiString RearID = Track->TrackElementAt(1399, AVE.RearStartOrRepeatMins).ElementID;
19084  AnsiString FrontID = Track->TrackElementAt(1400, AVE.FrontStartOrRepeatDigits).ElementID;
19085  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << ' ' << AVE.OtherHeadCode << '\n';
19086  }
19087  if(AVE.FormatType == SNSShuttle) //should all have been converted to chr
19088  {
19089  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
19090  }
19091  if(AVE.FormatType == Repeat) //shouldn't be any repeats, only here to show if any have been copied
19092  {
19093  VecFile << Marker << "Repeat " << AVE.RearStartOrRepeatMins << ' ' << AVE.FrontStartOrRepeatDigits << ' ' << AVE.NumberOfRepeats << '\n';
19094  }
19095  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
19096  {
19097  TActionVectorEntry AVHolder = AVE;
19098  if(AVE.Command.SubString(1,3) == "chr")
19099  {
19100  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "sp")
19101  {
19102  AVE.Command = "Change of service to " + AVE.OtherHeadCode + " after split";
19103  AVE.OtherHeadCode = "";
19104  }
19105  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns")
19106  {
19107  AVE.Command = "Change of service to ";
19108  }
19109  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns-sh")
19110  {
19111  AVE.Command = "Change to shuttle finishing service";
19112  }
19113  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "F-nshs")
19114  {
19115  AVE.Command = "Change to shuttle service " + AVE.OtherHeadCode + " from feeder";
19116  AVE.OtherHeadCode = "";
19117  }
19118  }
19119  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << '\n';
19120  AVE = AVHolder;
19121  }
19122  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
19123  {
19124  VecFile << Marker << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
19125  }
19126  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
19127  {
19128  VecFile << Marker << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
19129  }
19130  else if(AVE.FormatType == TimeTimeLoc)
19131  {
19132  VecFile << Marker << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
19133  }
19134  else if(AVE.FormatType == PassTime)
19135  {
19136  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
19137  }
19138  else if(AVE.FormatType == ExitRailway) //ListOfExits added at v2.10.0
19139  {
19140  AnsiString ListOfExits = "";
19141  for(TNumListIterator NLIt = AVE.ExitList.begin(); NLIt != AVE.ExitList.end(); NLIt++)
19142  {
19143  ListOfExits += AnsiString(Track->TrackElementAt(1432, *NLIt).ElementID) + ' ';
19144  }
19145  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << " Fer " << ListOfExits <<'\n';
19146  }
19147  else if(AVE.FormatType == FinRemHere)
19148  {
19149  VecFile << Marker << "Frh" << '\n';
19150  }
19151  }
19152  VecFile << '\n';
19153  }
19154  Utilities->CallLogPop(2318);
19155 }
19156 
19157 // ---------------------------------------------------------------------------
19158 
19159 TTrainDataEntry TTrainController::GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
19160 {
19161  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetServiceFromVector," + ServiceReference);
19162  FoundFlag = false;
19163  FinishType = true;
19164  for(unsigned int x = 0; x < Vector.size(); x++)
19165  {
19166  if(Vector.at(x).ServiceReference == ServiceReference)
19167  {
19168  if(Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1).FormatType == Repeat) //shouldn't be any repeats
19169  {
19170  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 2);
19171  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
19172  {
19173  FinishType = false;
19174  }
19175  }
19176  else
19177  {
19178  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1);
19179  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
19180  {
19181  FinishType = false;
19182  }
19183  }
19184  FoundFlag = true;
19185  Utilities->CallLogPop(2319);
19186  return(Vector.at(x));
19187  }
19188  }
19189  Utilities->CallLogPop(2320);
19190  return(Vector.at(Vector.size() - 1)); //return last for want of returning something
19191 }
19192 
19193 // ---------------------------------------------------------------------------
19194 
19195 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
19196 {
19197 //convert times to integer minutes
19198  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
19199  if((Time1 == "") || (Time2 == ""))
19200  {
19201  Utilities->CallLogPop(2213);
19202  return(false);
19203  }
19204  int Mins = Time1.SubString(4,2).ToInt();
19205  int Hours = Time1.SubString(1,2).ToInt();
19206  int Time1Mins = (Hours * 60) + Mins;
19207  Mins = Time2.SubString(4,2).ToInt();
19208  Hours = Time2.SubString(1,2).ToInt();
19209  int Time2Mins = (Hours * 60) + Mins;
19210  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
19211  {
19212  Utilities->CallLogPop(2214);
19213  return(true);
19214  }
19215  Utilities->CallLogPop(2215);
19216  return(false);
19217 }
19218 
19219 // ---------------------------------------------------------------------------
19220 
19221 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
19222  bool &AnalysisError, int &MaxNumberOfSameDirections)
19223 {
19224  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
19225 
19226  try
19227  {
19228  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
19229  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
19230  int SCPos = 0;
19231  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
19232  //first change every second comma in Input to a semicolon so can separate services but keep times with services
19233  bool EvenComma = false;
19234  for(int x = 1; x <= Input.Length(); x++)
19235  {
19236  TempStr1 = Input[x];
19237  if(TempStr1 == AnsiString(',') && EvenComma)
19238  {
19239  TempStr2 += ';';
19240  }
19241  else
19242  {
19243  TempStr2 += Input[x];
19244  }
19245  if(TempStr1 == AnsiString(','))
19246  {
19247  EvenComma = !EvenComma;
19248  }
19249  }
19250  //load up the list of services with associated times
19251  while(TempStr2.Length() > 0)
19252  {
19253  SCPos = TempStr2.Pos(';');
19254  if(SCPos > 0) //0 if not found, as won't be when only one service left
19255  {
19256  OneService = TempStr2.SubString(1, SCPos - 1);
19257  ServiceList.push_back(OneService);
19258  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
19259  }
19260  else //no semicolon so looking at last (or only) element
19261  {
19262  ServiceList.push_back(TempStr2);
19263  TempStr2 = "";
19264  }
19265  }
19266  ServiceList.sort(); // alphabetical order
19267  ServiceList.unique(); //remove duplicates
19268  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
19269 
19270  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
19271  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
19272  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
19273  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
19274 
19275  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
19276  {
19277  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
19278  }
19279  SLIt3 = ServiceList.end();
19280  SLIt3--; //so points to last element
19281  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
19282  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
19283  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
19284  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
19285  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
19286  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
19287 
19288  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
19289  {
19290  SLIt = SLIt1;
19291  SLIt++; //so points to one after SLIt1
19292  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
19293  {
19294  continue; //already allocated so skip to the next
19295  }
19296  else
19297  {
19298  CommaPos1 = SLIt1->Pos(','); //can't be 0
19299  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
19300  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
19301  SpacePos = ServiceRef1.Pos(' ');
19302  RepeatNum1 = 0;
19303  if(SpacePos > 0) //otherwise it's already correct
19304  {
19305  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
19306  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
19307  if(RepeatInfo1[1] == 'F')
19308  {
19309  RepeatNum1 = 0;
19310  }
19311  else
19312  {
19313  SpacePos = RepeatInfo1.Pos(' ');
19314  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
19315  }
19316  }
19317  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
19318  //but this includes the "&0" etc so need to strip these
19319  AmpersandPos = AnsiTime1.Pos('&');
19320  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
19321 
19322  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
19323  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
19324  {
19325  throw Exception("ASCLIt1 Error in " + Input);
19326  }
19327  ServiceCallingLocsList1 = ASCLIt1->second;
19328  AmpersandPos = SLIt1->Pos('&');
19329  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
19330  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
19331 
19332  SameDirectionCount = 1;
19333  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
19334  {
19335  CommaPos2 = SLIt2->Pos(','); //can't be 0
19336  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
19337  //but this contains "(First service..." etc so need to strip these
19338  SpacePos = ServiceRef2.Pos(' ');
19339  RepeatNum2 = 0;
19340  if(SpacePos > 0) //otherwise it's already correct
19341  {
19342  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
19343  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
19344  if(RepeatInfo2[1] == 'F')
19345  {
19346  RepeatNum2 = 0;
19347  }
19348  else
19349  {
19350  SpacePos = RepeatInfo2.Pos(' ');
19351  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
19352  }
19353  }
19354  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
19355  //but this includes the "&0" etc so need to strip these
19356  AmpersandPos = AnsiTime2.Pos('&');
19357  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
19358 
19359  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
19360  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
19361  {
19362  throw Exception("ASCLIt2 Error in " + Input);
19363  }
19364  ServiceCallingLocsList2 = ASCLIt2->second;
19365  //now compare the two
19366  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
19367  {
19368  int AmpersandPos = SLIt2->Pos('&');
19369  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
19370  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
19371  SameDirectionCount++;
19372  }
19373  }
19374  if(SameDirectionCount > MaxNumberOfSameDirections)
19375  {
19376  MaxNumberOfSameDirections = SameDirectionCount;
19377  }
19378  }
19379  }
19380 
19381  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
19382  {
19383  //any existing direction so allocate it now
19384  AmpersandPos = SLIt3->Pos('&');
19385  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
19386  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
19387  }
19388  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
19389  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
19390  {
19391  //extract the DirectionMarker as an integer
19392  AmpersandPos = SLIt->Pos('&');
19393  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
19394  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
19395  DirectionMarker = DirectionMarkerString.ToInt();
19396  AnsiString DirectionSuffix = "";
19397  char c;
19398  if(DirectionMarker < 27)
19399  {
19400  c = 64 + DirectionMarker; //so 1 -> 'A'
19401  DirectionSuffix = "," + AnsiString(c);
19402  }
19403  else if(DirectionMarker < 53)
19404  {
19405  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
19406  DirectionSuffix = ",A" + AnsiString(c);
19407  }
19408  else
19409  {
19410  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
19411  }
19412  *SLIt = ServiceWithoutMarker + DirectionSuffix;
19413  }
19414  //now prepare the final consolidated output
19415  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
19416  {
19417  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
19418  }
19419  if(Output.Length() > 0)
19420  {
19421  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
19422  }
19423  Utilities->CallLogPop(2216);
19424  return(Output);
19425  }
19426 
19427  catch(const Exception &e) //non error catch
19428  {
19429  AnalysisError = true;
19430  Utilities->CallLogPop(2227);
19431  return(e.Message);
19432  }
19433 }
19434 
19435 // ---------------------------------------------------------------------------
19436 
19437 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
19438 {
19439  //similar to above but doesn't include times in the input
19440  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
19441  AnsiString InternalInput = Input, Output = "", OneService = "";
19442  int CommaPos = 0;
19443  std::list<AnsiString> ServiceList;
19444  //load up the list
19445  while(InternalInput.Length() > 0)
19446  {
19447  CommaPos = InternalInput.Pos(',');
19448  if(CommaPos > 0) //0 if not found, as won't be when only one service left
19449  {
19450  OneService = InternalInput.SubString(1, CommaPos - 1);
19451  ServiceList.push_back(OneService);
19452  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
19453  }
19454  else //no comma so looking at last (or only) element
19455  {
19456  ServiceList.push_back(InternalInput);
19457  InternalInput = "";
19458  }
19459  }
19460 
19461  ServiceList.sort(); // alphabetical order
19462  ServiceList.unique(); //remove duplicates
19463  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
19464  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
19465  {
19466  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
19467  }
19468  if(Output.Length() > 0)
19469  {
19470  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
19471  }
19472  Utilities->CallLogPop(2217);
19473  return(Output);
19474 }
19475 
19476 // ---------------------------------------------------------------------------
19477 
19478 
19479 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
19480  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
19481 {
19482  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
19483  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
19484 
19485  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
19486  //general list pointers, LocPtrs point to Location in the two lists
19487 
19488  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
19489  //for List1
19490  bool LocFound = false;
19491  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
19492  int IncMinutes;
19493  TDateTime FirstServiceTime;
19494 
19495  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
19496  int Ref1Target = 0, Ref1Count = 0;
19497  int SlashPos = Ref1.Pos('/');
19498  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
19499  {
19500  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
19501  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
19502  }
19503  int Ref2Target = 0, Ref2Count = 0;
19504  SlashPos = Ref2.Pos('/');
19505  if(SlashPos > 0) //if 0 leave as is
19506  {
19507  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
19508  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
19509  }
19510  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
19511  {
19512  //even if others have same names. But if there are cdt's then need to refind the correct service
19513  if((*ListPtr1) == Location) //
19514  {
19515  LocPtr1 = ListPtr1; //may be modified later
19516  LocFound = true;
19517  }
19518  if(ListPtr1->SubString(1, 3) == "%%%")
19519  {
19520  AnsiString CDTTime = ListPtr1->SubString(4, 5);
19521  //now adjust the time to correspond to the repeat if there is one
19522  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
19523  {
19524  IncMinutes = -1;
19525  FirstServiceTime = TDateTime(-1);
19526  bool BreakFlag = false;
19527  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
19528  {
19529  if(TDVIt->ServiceReference == Ref1)
19530  {
19531  if(Ref1Target > Ref1Count)
19532  {
19533  Ref1Count++;
19534  continue;
19535  }
19536  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
19537  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
19538  {
19539  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
19540  {
19541  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
19542  BreakFlag = true;
19543  break;
19544  }
19545  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
19546  {
19547  FirstServiceTime = AVIt->ArrivalTime;
19548  BreakFlag = true;
19549  break;
19550  }
19551  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
19552  {
19553  FirstServiceTime = AVIt->DepartureTime;
19554  BreakFlag = true;
19555  break;
19556  }
19557  }
19558  if(BreakFlag)
19559  {
19560  break;
19561  }
19562  }
19563  }
19564  if(IncMinutes == -1)
19565  {
19566  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
19567  }
19568  if(FirstServiceTime == TDateTime(-1))
19569  {
19570  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
19571  }
19572  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
19573  }
19574  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
19575  {
19576  LocFound = false;
19577  continue;
19578  }
19579  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
19580  {
19581  break;
19582  }
19583  if(Time1 > CDTTime) //not there yet so go on
19584  {
19585  LocFound = false;
19586  continue;
19587  }
19588  if(Time1 < CDTTime) //gone too far so can stop now
19589  {
19590  break;
19591  }
19592  }
19593  }
19594  if(!LocFound) //have to find it in both lists
19595  {
19596  Utilities->CallLogPop(2228);
19597  return( false);
19598  }
19599  //for List2
19600  LocFound = false;
19601  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
19602  {
19603  if((*ListPtr2) == Location)
19604  {
19605  LocPtr2 = ListPtr2; //may be modified later
19606  LocFound = true;
19607  }
19608  if(ListPtr2->SubString(1, 3) == "%%%")
19609  {
19610  AnsiString CDTTime = ListPtr2->SubString(4, 5);
19611  //now adjust the time to correspond to the repeat if there is one
19612  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
19613  {
19614  IncMinutes = -1;
19615  FirstServiceTime = TDateTime(-1);
19616  bool BreakFlag = false;
19617  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
19618  {
19619  if(TDVIt->ServiceReference == Ref2)
19620  {
19621  if(Ref2Target > Ref2Count)
19622  {
19623  Ref2Count++;
19624  continue;
19625  }
19626  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
19627  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
19628  {
19629  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
19630  {
19631  FirstServiceTime = AVIt->EventTime;
19632  BreakFlag = true;
19633  break;
19634  }
19635  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
19636  {
19637  FirstServiceTime = AVIt->ArrivalTime;
19638  BreakFlag = true;
19639  break;
19640  }
19641  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
19642  {
19643  FirstServiceTime = AVIt->DepartureTime;
19644  BreakFlag = true;
19645  break;
19646  }
19647  }
19648  if(BreakFlag)
19649  {
19650  break;
19651  }
19652  }
19653  }
19654  if(IncMinutes == -1)
19655  {
19656  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
19657  }
19658  if(FirstServiceTime == TDateTime(-1))
19659  {
19660  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
19661  }
19662  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
19663  }
19664  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
19665  {
19666  LocFound = false;
19667  continue;
19668  }
19669  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
19670  {
19671  break;
19672  }
19673  if(Time2 > CDTTime) //not there yet so go on
19674  {
19675  LocFound = false;
19676  continue;
19677  }
19678  if(Time2 < CDTTime) //gone too far so can stop now
19679  {
19680  break;
19681  }
19682  }
19683  }
19684  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
19685  {
19686  Utilities->CallLogPop(2229);
19687  return( false);
19688  }
19689  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
19690  //set ListPtr1 to the search start position
19691  if(Arrival)
19692  {
19693  LP1 = List1.begin();
19694  LP1--; //now points to before the first entry
19695  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
19696  {
19697  if(ListPtr1 == List1.begin())
19698  {
19699  break;
19700  }
19701  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
19702  {
19703  ListPtr1++; //point to one past the cdt
19704  break;
19705  }
19706  }
19707  //set ListPtr2 to the search start position
19708  LP2 = List2.begin();
19709  LP2--; //now points to before the first entry
19710  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
19711  {
19712  if(ListPtr2 == List2.begin())
19713  {
19714  break;
19715  }
19716  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
19717  {
19718  ListPtr2++; //point to one past the cdt
19719  break;
19720  }
19721  }
19722  //ListPtr1 & 2 now at search start position
19723  LP1 = ListPtr1;
19724  LP2 = ListPtr2;
19725  //now search forwards, i.e. for common locations before Location
19726  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
19727  {
19728  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
19729  {
19730  break;
19731  }
19732  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
19733  {
19734  break;
19735  }
19736  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
19737  {
19738  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
19739  {
19740  break;
19741  }
19742  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
19743  {
19744  break;
19745  }
19746  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
19747  {
19748  Utilities->CallLogPop(2230);
19749  return( true);
19750  }
19751  }
19752  }
19753  }
19754 
19755  //now, for the departure analysis, reset the start positions and search locations after Location
19756 
19757  else
19758  {
19759  LP1 = LocPtr1;
19760  LP1++; //start at one past the location itself
19761  LP2 = LocPtr2;
19762  LP2++;
19763  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
19764  {
19765  if(ListPtr1 == List1.end()) //reached end point so stop
19766  {
19767  break;
19768  }
19769  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
19770  {
19771  break;
19772  }
19773  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
19774  {
19775  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
19776  {
19777  break;
19778  }
19779  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
19780  {
19781  break;
19782  }
19783  if((*ListPtr1) == (*ListPtr2)) //found a common later location
19784  {
19785  Utilities->CallLogPop(2231);
19786  return( true);
19787  }
19788  }
19789  }
19790  }
19791  Utilities->CallLogPop(2232);
19792  return( false);
19793 }
19794 
19795 // ---------------------------------------------------------------------------
19796 
19797 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
19798 {
19799  // changed at v2.7.0 to show allowable exit elements
19800  if(ExitList.empty())
19801  {
19802  return("");
19803  }
19804  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
19805  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
19806  AnsiString ExitLocList = "";
19807  AllowedExits = "";
19808 
19809  unsigned int Counter = 0;
19810  for(TNumListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
19811  {
19812  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
19813  Counter++;
19814  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
19815  {
19816  ExitLocList += "\n";
19817  }
19818  }
19819  if(StartName == "")
19820  {
19821  if(ExitList.size() == 1)
19822  {
19823  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
19824  Utilities->CallLogPop(1571);
19825  return(" at " + ID);
19826  }
19827  else
19828  {
19829  Utilities->CallLogPop(1572);
19830  if(ExitList.size() < 4)
19831  {
19832  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
19833  return("");
19834  }
19835  else
19836  {
19837  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
19838  return("");
19839  }
19840  }
19841  }
19842  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
19843  {
19844  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
19845  {
19846  Utilities->CallLogPop(1570);
19847  if(ExitList.size() < 4)
19848  {
19849  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
19850  return("");
19851  }
19852  else
19853  {
19854  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
19855  return("");
19856  }
19857  }
19858  }
19859  Utilities->CallLogPop(1569);
19860  if(ExitList.size() < 4)
19861  {
19862  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
19863  return(" at " + StartName);
19864  }
19865  else
19866  {
19867  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
19868  return(" at " + StartName);
19869  }
19870 }
19871 
19872 // ---------------------------------------------------------------------------
19873 /* can't trust this as locations within a vector may not be contiguous
19874  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
19875  {
19876  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
19877  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
19878  //must be preceded by a TimeLoc departure
19879  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
19880  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
19881  {
19882  if((AVPtr + x) < TDEPtr->ActionVector.end())
19883  {
19884  AnsiString xx = (AVPtr + x)->Command;//test
19885  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
19886  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
19887  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
19888  {
19889  Utilities->CallLogPop();
19890  return false;
19891  }
19892  else if((AVPtr + x)->SequenceType == Finish)
19893  {
19894  Utilities->CallLogPop();
19895  return true;
19896  }
19897  }
19898  }
19899  Utilities->CallLogPop();
19900  return false;
19901  }
19902 */
19903 // ---------------------------------------------------------------------------
19904 
19905 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
19906 {
19907  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
19908  AnsiString FormatStr = "####0.0";
19909  AnsiString AvLateArrMins = "";
19910  AnsiString AvEarlyArrMins = "";
19911  AnsiString AvLatePassMins = "";
19912  AnsiString AvEarlyPassMins = "";
19913  AnsiString AvLateDepMins = "";
19914  AnsiString AvLateExitMins = "";
19915  AnsiString AvEarlyExitMins = "";
19916 
19917  //calculate remaining CumulativeDelayedRandMinsAllTrains for trains still in vector (CumulativeDelayedRandMinsAllTrains for exited or removed trains already accounted for)
19918  for(unsigned int x = 0; x < TrainVector.size(); x++)
19919  {
19920  Utilities->CumulativeDelayedRandMinsAllTrains += int(TrainVectorAt(89, x).CumulativeDelayedRandMinsOneTrain);
19921  }
19922 
19923  if(LateArrivals > 0)
19924  {
19925  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
19926  }
19927  if(EarlyArrivals > 0)
19928  {
19929  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
19930  }
19931  if(LatePasses > 0)
19932  {
19933  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
19934  }
19935  if(EarlyPasses > 0)
19936  {
19937  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
19938  }
19939  if(LateDeps > 0)
19940  {
19941  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
19942  }
19943  if(LateExits > 0) //added at v2.9.1
19944  {
19945  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
19946  }
19947  if(EarlyExits > 0) //added at v2.9.1
19948  {
19949  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
19950  }
19951  PerfFile << '\n' << '\n' << "***************************************";
19952  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
19953 
19954  if(OnTimeArrivals != 1)
19955  {
19956  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
19957  }
19958  else
19959  {
19960  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
19961  }
19962  if(LateArrivals > 1)
19963  {
19964  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
19965  }
19966  else if(LateArrivals == 1)
19967  {
19968  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
19969  }
19970  else
19971  {
19972  PerfFile << LateArrivals << " late arrivals" << '\n';
19973  }
19974  if(EarlyArrivals > 1)
19975  {
19976  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
19977  }
19978  else if(EarlyArrivals == 1)
19979  {
19980  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
19981  }
19982  else
19983  {
19984  PerfFile << EarlyArrivals << " early arrivals" << '\n';
19985  }
19986  if(OnTimePasses != 1)
19987  {
19988  PerfFile << OnTimePasses << " on-time passes" << '\n';
19989  }
19990  else
19991  {
19992  PerfFile << OnTimePasses << " on-time pass" << '\n';
19993  }
19994  if(LatePasses > 1)
19995  {
19996  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
19997  }
19998  else if(LatePasses == 1)
19999  {
20000  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
20001  }
20002  else
20003  {
20004  PerfFile << LatePasses << " late passes" << '\n';
20005  }
20006  if(EarlyPasses > 1)
20007  {
20008  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
20009  }
20010  else if(EarlyPasses == 1)
20011  {
20012  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
20013  }
20014  else
20015  {
20016  PerfFile << EarlyPasses << " early passes" << '\n';
20017  }
20018 
20019  if(OnTimeExits != 1) //this batch added at v2.9.1
20020  {
20021  PerfFile << OnTimeExits << " on-time exits" << '\n';
20022  }
20023  else
20024  {
20025  PerfFile << OnTimeExits << " on-time exit" << '\n';
20026  }
20027  if(LateExits > 1)
20028  {
20029  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
20030  }
20031  else if(LateExits == 1)
20032  {
20033  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
20034  }
20035  else
20036  {
20037  PerfFile << LateExits << " late exits" << '\n';
20038  }
20039  if(EarlyExits > 1)
20040  {
20041  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
20042  }
20043  else if(EarlyExits == 1)
20044  {
20045  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
20046  }
20047  else
20048  {
20049  PerfFile << EarlyExits << " early exits" << '\n';
20050  }
20051 
20052  if(OnTimeDeps != 1)
20053  {
20054  PerfFile << OnTimeDeps << " on-time departures" << '\n';
20055  }
20056  else
20057  {
20058  PerfFile << OnTimeDeps << " on-time departure" << '\n';
20059  }
20060  if(LateDeps > 1)
20061  {
20062  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
20063  }
20064  else if(LateDeps == 1)
20065  {
20066  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
20067  }
20068  else
20069  {
20070  PerfFile << LateDeps << " late departures" << '\n';
20071  }
20072  TDateTime TempExcessLCDownTime;
20073  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
20074  {
20075 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
20076  //later perf summaries with lower values, changed at v2.8.0
20077 // {
20078  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
20079 // }
20080 /*
20081  else
20082  {
20083  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
20084  }
20085 */
20086  if(TempExcessLCDownTime > TDateTime(0))
20087  {
20088  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
20089  }
20090  }
20091 
20092  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
20093 
20094  if(ExcessLCDownMins > 0.1)
20095  {
20096  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
20097  }
20098  if(Utilities->CumulativeDelayedRandMinsAllTrains > 0) //added at v2.13.0
20099  {
20100  PerfFile << Utilities->CumulativeDelayedRandMinsAllTrains << " minutes lost due to random delays when stopped at locations" << '\n';
20101  }
20102  if(MissedStops != 1)
20103  {
20104  PerfFile << MissedStops << " missed stops" << '\n';
20105  }
20106  else
20107  {
20108  PerfFile << MissedStops << " missed stop" << '\n';
20109  }
20110  if(OtherMissedEvents != 1)
20111  {
20112  PerfFile << OtherMissedEvents << " other missed events" << '\n';
20113  }
20114  else
20115  {
20116  PerfFile << OtherMissedEvents << " other missed event" << '\n';
20117  }
20118  if(SkippedTTEvents != 1)
20119  {
20120  PerfFile << SkippedTTEvents << " skipped timetable events" << '\n';
20121  }
20122  else
20123  {
20124  PerfFile << SkippedTTEvents << " skipped timetable event" << '\n';
20125  }
20126  if(UnexpectedExits != 1)
20127  {
20128  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
20129  }
20130  else
20131  {
20132  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
20133  }
20134  if(IncorrectExits != 1)
20135  {
20136  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
20137  }
20138  else
20139  {
20140  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
20141  }
20142  if(NumFailures != 1)
20143  {
20144  PerfFile << NumFailures << " train failures" << '\n';
20145  }
20146  else
20147  {
20148  PerfFile << NumFailures << " train failure" << '\n';
20149  }
20150  if(AvHoursIntValue > 0)
20151  {
20152  if(AvHoursIntValue == 1)
20153  {
20154  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
20155  }
20156  else
20157  {
20158  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
20159  }
20160  }
20161  AnsiString AvLateMinsLocsNotReached = "";
20162 
20164  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
20165  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
20166 
20167  if(LocsNotReached > 0)
20168  {
20169  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
20170  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
20171  }
20172  if(SPADRisks != 1)
20173  {
20174  PerfFile << SPADRisks << " SPAD risks" << '\n';
20175  }
20176  else
20177  {
20178  PerfFile << SPADRisks << " SPAD risk" << '\n';
20179  }
20180  if(SPADEvents != 1)
20181  {
20182  PerfFile << SPADEvents << " SPADs" << '\n';
20183  }
20184  else
20185  {
20186  PerfFile << SPADEvents << " SPAD" << '\n';
20187  }
20188  if(Derailments != 1)
20189  {
20190  PerfFile << Derailments << " derailments" << '\n';
20191  }
20192  else
20193  {
20194  PerfFile << Derailments << " derailment" << '\n';
20195  }
20196  if(CrashedTrains != 1)
20197  {
20198  PerfFile << CrashedTrains << " crashed trains" << '\n';
20199  }
20200  else
20201  {
20202  PerfFile << CrashedTrains << " crashed train" << '\n';
20203  }
20204  PerfFile << '\n' << "***************************************" << '\n';
20205 
20206  bool DerailSPADFlag = false, CrashFlag = false;
20207 
20208  int OverallScorePercent = 100;
20209  int TotArrDepExit = 0;
20210  double TotLateMinsFactor = 1;
20211  double MissedStopAndSPADRiskFactor = 1;
20212  double NetNegFactor = 1;
20213 
20215  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1, passes not counted
20216  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
20217  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
20218  // 'no timetabled departures... message, which was inappropriate
20219 
20220  if((SPADEvents > 0) || (Derailments > 0))
20221  {
20222  OverallScorePercent = 5; // overrides other calculations
20223  DerailSPADFlag = true;
20224  }
20225  if(CrashedTrains > 0)
20226  {
20227  OverallScorePercent = 0; // overrides other calculations
20228  CrashFlag = true;
20229  }
20230  if(OverallScorePercent == 100)
20231  {
20232  int LatenessPenalty = TotLateArrMins + TotLateDepMins; //added at v2.13.0 for random delays
20233  if(Utilities->CumulativeDelayedRandMinsAllTrains > LatenessPenalty)
20234  {
20235  LatenessPenalty = 0;
20236  }
20237  else
20238  {
20239  LatenessPenalty -= Utilities->CumulativeDelayedRandMinsAllTrains;
20240  }
20241  if(TotArrDepExit > 0)
20242  {
20243  TotLateMinsFactor = exp((-0.1732) * (LatenessPenalty + OperatingTrainLateMins + NotStartedTrainLateMins + TotLateExitMins +
20244  ((OtherMissedEvents + SkippedTTEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
20245  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
20246  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
20247  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
20248  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
20249  // of arrivals & departures, where 4% = half, 8% = a quarter etc
20250  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
20251  // NetNegfactor: product of the above two
20252  OverallScorePercent = 100 * NetNegFactor;
20253  }
20254  }
20255  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
20256  // flag condits added at v1.1.4 - see above for what the error was
20257  {
20258  AnsiString OneFailureString = ", though the failure would account for some poor performance";
20259  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
20260  AnsiString AddedString = "";
20261  if(NumFailures == 1)
20262  {
20263  AddedString = OneFailureString;
20264  }
20265  if(NumFailures > 1)
20266  {
20267  AddedString = TwoOrMoreFailureString;
20268  }
20269  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
20270  AnsiString Rating = "";
20271  if(OverallScorePercent == 100)
20272  {
20273  Rating = "Perfect!";
20274  }
20275  else if(OverallScorePercent >= 95)
20276  {
20277  Rating = "Excellent";
20278  }
20279  else if(OverallScorePercent >= 90)
20280  {
20281  Rating = "Very good";
20282  }
20283  else if(OverallScorePercent >= 80)
20284  {
20285  Rating = "Good";
20286  }
20287  else if(OverallScorePercent >= 70)
20288  {
20289  Rating = "Fair";
20290  }
20291  else if(OverallScorePercent >= 60)
20292  {
20293  Rating = "Unacceptable" + AddedString;
20294  }
20295  else if(OverallScorePercent >= 50)
20296  {
20297  Rating = "Poor" + AddedString;
20298  }
20299  else if(OverallScorePercent >= 40)
20300  {
20301  Rating = "Bad" + AddedString;
20302  }
20303  else if(OverallScorePercent >= 30)
20304  {
20305  Rating = "Very bad" + AddedString;
20306  }
20307  else if(OverallScorePercent >= 20)
20308  {
20309  Rating = "Terrible" + AddedString;
20310  }
20311  else if(OverallScorePercent >= 10)
20312  {
20313  Rating = "Appalling" + AddedString;
20314  }
20315  else if(OverallScorePercent >= 5)
20316  {
20317  if(DerailSPADFlag)
20318  {
20319  Rating = "Disastrous - potential loss of life";
20320  }
20321  // SPADs/Derailments
20322  else
20323  {
20324  Rating = "Dire" + AddedString;
20325  }
20326  }
20327  else if(OverallScorePercent < 5)
20328  {
20329  if(CrashFlag)
20330  {
20331  Rating = "Catastrophic - loss of life"; // Crashes
20332  }
20333  else
20334  {
20335  Rating = "Abysmal";
20336  }
20337  }
20338  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
20339  }
20340  else
20341  {
20342  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
20343  }
20344  PerfFile << '\n' << "***************************************";
20345  PerfFile.flush();
20346  Utilities->CallLogPop(1736);
20347 }
20348 
20349 // ---------------------------------------------------------------------------
20350 
20352 {
20353  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
20354  for(unsigned int x = 0; x < TrainVector.size(); x++)
20355  {
20356  TTrain &Train = TrainVectorAt(58, x);
20357  if(Train.Crashed)
20358  // can't use background colours for crashed & derailed because same colour
20359  {
20360  CrashWarning = true;
20361  }
20362  else if(Train.Derailed)
20363  // can't use background colours for crashed & derailed because same colour
20364  {
20365  DerailWarning = true;
20366  }
20367  else if(Train.BackgroundColour == clSPADBackground)
20368  // use colour as that changes as soon as passes signal
20369  {
20370  SPADWarning = true;
20371  }
20372  else if(Train.BackgroundColour == clTrainFailedBackground)
20373  {
20374  TrainFailedWarning = true;
20375  }
20376  else if(Train.BackgroundColour == clCallOnBackground)
20377  // use colour as also stopped at signal
20378  {
20379  CallOnWarning = true;
20380  }
20381  else if(Train.BackgroundColour == clSignalStopBackground)
20382  // use colour to distinguish from call-on
20383  {
20384  SignalStopWarning = true;
20385  }
20386  else if(Train.BackgroundColour == clBufferAttentionNeeded)
20387  // use colour to distinguish from ordinary buffer stop
20388  {
20389  BufferAttentionWarning = true;
20390  }
20391  }
20392  Utilities->CallLogPop(1796);
20393 }
20394 
20395 // ---------------------------------------------------------------------------
20396 
20398 {
20399  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
20400 
20401  // calculate lateness for running trains
20404  for(unsigned int x = 0; x < TrainVector.size(); x++)
20405  {
20406  TTrain &Train = TrainVectorAt(64, x);
20407  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
20408  AVEntryPtr++)
20409  {
20410  if(AVEntryPtr < Train.ActionVectorEntryPtr)
20411  {
20412  continue;
20413  }
20414  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.RevisedStoppedAtLoc() && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
20415  TTClockTime))
20416  {
20417  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
20419  }
20420 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
20421  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
20422  TTClockTime))
20423  {
20424  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
20425  OperatingTrainArrDep++;
20426  }
20427 */
20428  }
20429  }
20430 
20431  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
20434 
20435  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
20436  {
20437  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
20438  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
20439  int IncrementalMinutes = 0;
20440  if(AVEntryLast.FormatType == Repeat)
20441  {
20442  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
20443  }
20444  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
20445  {
20446  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
20447  if(TTOD.RunningEntry != NotStarted)
20448  {
20449  continue;
20450  }
20451  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
20452  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
20453  bool TrainOperatingFlag = false;
20454  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
20455  {
20456  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
20457  {
20458  TrainOperatingFlag = true;
20459  break;
20460  }
20461  }
20462  if(TrainOperatingFlag)
20463  {
20464  continue;
20465  }
20466  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
20467  {
20468  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
20469  }
20470  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
20471  {
20472  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
20473  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
20474  {
20475  break; // all the rest will also be greater (& default of -1 will be less)
20476  }
20477  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
20478  {
20479  break; // all the rest will also be greater (& default of -1 will be less)
20480  }
20481  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
20482  {
20483  break; // all the rest will also be greater (& default of -1 will be less)
20484  }
20485  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
20486  {
20487  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
20489  }
20490 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
20491  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
20492  {
20493  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
20494  NotStartedTrainArrDep++;
20495  }
20496 */
20497  }
20498  }
20499  }
20500  Utilities->CallLogPop(1894);
20501 }
20502 
20503 // ---------------------------------------------------------------------------
20504 
20506 // new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2.13.0)
20507 // clears entries then adds values for running trains then for continuation entries
20508 // dont limit size here as need to check all trains (ActionsDueListBox is limited to 20 trains in Interface.cpp)
20509 {
20510  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
20511  OpTimeToActMultiMap.clear();
20512  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
20513 
20514  if(!TrainVector.empty())
20515  // build OpTimeToActMultiMap entries for running trains
20516  {
20517  AnsiString HeadCode;
20518  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
20519  int TrainID;
20520  THCandTrainPosParam HCandTrainPosParam;
20521  for(unsigned int x = 0; x < TrainVector.size(); x++)
20522  {
20523  HeadCode = TrainVectorAt(62, x).HeadCode;
20524  TrainID = TrainVectorAt(63, x).TrainID;
20525  HCandTrainPosParam.first = HeadCode;
20526  HCandTrainPosParam.second = TrainID;
20527  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
20528  if((TimeToAct >= 0) && (TimeToAct < 59.9))
20529  // -1 indicates don't display
20530  {
20531  OpTimeToActMultiMapEntry.first = TimeToAct;
20532  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
20533  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
20534  }
20535  }
20536  }
20537 /*
20538  * class TContinuationTrainExpectationEntry
20539  {
20540  public:
20541  AnsiString Description; ///< service description
20542  AnsiString HeadCode; ///< service headcode
20543  int RepeatNumber; ///< service RepeatNumber
20544  int IncrementalMinutes; ///< Repeat separation in minutes
20545  int IncrementalDigits; ///< Repeat headcode separation
20546  int VectorPosition; ///< TrackVectorPosition for the continuation element
20547  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
20548  };
20549 
20550  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
20551  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
20552  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
20553  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
20554 */
20555 
20557  // build OpTimeToActMultiMap entries for expected trains
20558  {
20559  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
20560  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
20561  float TimeToAct = 0; // minutes
20562  int DistanceToRedSignal = 0; // metres
20563  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
20564  // used to ensure only one train displayed for a given continuation
20565  ContinuationEntryVecPosVector.clear();
20566  bool LaterTrain = false;
20569  {
20570  LaterTrain = false;
20571  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
20572  {
20573  CTEIt++;
20574  continue; // not interested in running or exited trains
20575  }
20576  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
20577  {
20578  CTEIt++;
20579  continue;
20580  // don't include trains not entered yet when a train is already on the continuation
20581  }
20582  if(!ContinuationEntryVecPosVector.empty())
20583  {
20584  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
20585  {
20586  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
20587  {
20588  LaterTrain = true;
20589  ;
20590  // skip past remaining trains waiting to enter at same point
20591  break;
20592  }
20593  }
20594  }
20595  if(LaterTrain)
20596  {
20597  CTEIt++;
20598  continue;
20599  }
20600  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
20601  AnsiString HeadCode = CTEIt->second.HeadCode;
20602  float CurrentStopTime; // set to 0 at start of function
20603  float LaterStopTime; // set to 0 at start of function
20604  float RecoverableTime; // set to 0 at start of function
20605  int AvTrackSpeed; // set to 0 at start of function
20606  int TrainID = -1; // not yet allocated for train still to enter
20607  int DistanceToExit; //not used for continuation entries
20608  THVShortPair ExitPair;
20609  bool SigControlAndCanPassRedSignal = false; // doesn't apply for a continuation
20610 
20611 //at v2.11.0 found that with *AVPtr set to ...ActionVector.at(0) below instead of ...at(1) to stop signaller control trains throwing an error (because there is no ...at(1) -
20612 //discovered with Birmingham) the LaterStopTime isn't calculated and if a train does something other than depart after an arrival it is still listed in the actions due panel -
20613 //because it just calcs the distance to the red signal and converts that to a time. So here (after v2.11.0) this new test is introduced to determine whether a train is a
20614 //signaller control train (when the ActionVector size is 1) or not (ActionVector size > 1), and the ...at(value) is set accordingly - 0 for signaller control or 1 if not.
20615 
20616  int AtValue = 1;
20617  if(CTEIt->second.TrainDataEntryPtr->ActionVector.size() == 1)
20618  {
20619  AtValue = 0;
20620  }
20621  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
20622  // EntryPos always 0 for entering at a continuation
20623  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(AtValue), //see above note
20624  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
20625  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
20626  // for a train it's the one in front of LeadElement
20627  if(AvTrackSpeed < 30)
20628  {
20629  AvTrackSpeed = 30;
20630  }
20631  if(DistanceToRedSignal == -1)
20632  {
20633  TimeToAct = 60.0;
20634  }
20635  else
20636  {
20637  int Speed = AvTrackSpeed;
20638  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
20639  if(AvTrackSpeed > MaxSpeed)
20640  {
20641  Speed = MaxSpeed;
20642  }
20643  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(0).SignallerControl) //changed to ...at(0) from at(1) at v2.11.0 as SignallerControl only valid for ..at(0)
20644  // defined in timetable as under signaller control
20645  {
20646  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
20647  LaterStopTime = 0;
20648  }
20649  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
20650  // accel & decel taken into account in
20651  // CalcDistanceToRedSignalandStopTime
20652  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
20653  // don't need CurrentStopTime or RecoverableTime for continuation entries
20654  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
20655  TimeToAct += MinsBefEnter;
20656  }
20657  THCandTrainPosParam HCandTrainPosParam;
20658  HCandTrainPosParam.first = HeadCode;
20659  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
20660  // -1-CTE... because 2nd value covers TrainID if +ve &
20661  // continuation track vector position if -ve, -1 allows for vecpos being 0
20662  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
20663  {
20664  OpTimeToActMultiMapEntry.first = TimeToAct;
20665  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
20666  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
20667  }
20668  CTEIt++;
20669  }
20670  }
20671  Utilities->CallLogPop(2081);
20672 }
20673 
20674 // ---------------------------------------------------------------------------
20675 
20677 // new for multiplayer
20678 // clears entries then adds values for running trains
20679 {
20680  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildTimeToExitMultiMap");
20681  TimeToExitMultiMap.clear();
20682  TTimeToExitMultiMapEntry TimeToExitMultiMapEntry;
20683 
20684  if(!TrainVector.empty())
20685  // build map entries for running trains
20686  {
20687  TExitInfo ExitInfo; //corresponds to TServiceInfo in Interface
20688  THVShortPair ExitPair;
20689  float TimeToExit;
20690  for(unsigned int x = 0; x < TrainVector.size(); x++)
20691  {
20693  ExitInfo.RepeatNumber = short(TrainVectorAt(81, x).RepeatNumber);
20694  ExitInfo.TimeToExitSecs = short(TrainVectorAt(77, x).TimeToExit * 60);
20695  ExitPair = TrainVectorAt(76, x).ExitPair;
20696  if((ExitInfo.TimeToExitSecs >= 3570) || (ExitInfo.TimeToExitSecs < 1)) //59.5 mins or -60 secs
20697  {
20698  ExitInfo.TimeToExitSecs = -1;
20699  }
20700  TimeToExitMultiMapEntry.first = ExitPair;
20701  TimeToExitMultiMapEntry.second = ExitInfo;
20702  TimeToExitMultiMap.insert(TimeToExitMultiMapEntry);
20703  }
20704  }
20705  Utilities->CallLogPop(2323);
20706 }
20707 
20708 // ---------------------------------------------------------------------------
20709 
20710 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
20711  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
20712  float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
20713 // new v2.2.0
20714 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
20715 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
20716 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
20717 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
20718 // aren't used - this means there is no display for the train in question
20719 {
20720  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
20721  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
20722  int DistanceToRedSignal = 0;
20723  DistanceToExit = -1;
20724  ExitPair.first = -1;
20725  ExitPair.second = -1;
20726  int CumTrackSpeed = 0;
20727  // average track speed, in case need to use in time calc
20728  int TrackSpeedCount = 0;
20729  float KmPerLocationStop;
20730  float MaxAllowableSpeed;
20731 
20732  //below added at v2.6.1
20733  if(TrainID > -1)
20734  {
20735  TTrain &Train = TrainVectorAtIdent(51, TrainID);
20736  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
20737  Train.StationStopCalculated = false;
20738  }
20739  AvTrackSpeed = 0;
20740  int CurrentElement = TrackVectorPosition;
20741  int CurrentEntryPos = TrackVectorPositionEntryPos;
20742  int NextElement;
20743  int NextEntryPos;
20744  int NextExitPos;
20745 
20746  CurrentStopTime = 0;
20747  LaterStopTime = 0;
20748  RecoverableTime = 0;
20749  if(CurrentElement == -1) // end element, no action needed
20750  {
20751  Utilities->CallLogPop(2094);
20752  return(-1);
20753  }
20754  int CurrentExitPos;
20755 
20756  // get ExitPos for first element to be measured
20757  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
20758  {
20759  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
20760  {
20761  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
20762  {
20763  CurrentExitPos = 1;
20764  }
20765  else
20766  {
20767  CurrentExitPos = 3;
20768  }
20769  }
20770  else
20771  {
20772  CurrentExitPos = 0; // trailing point
20773  }
20774  }
20775  else
20776  {
20777  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
20778  }
20779  // get CumTrackSpeed for first measured element
20780 
20781  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
20782  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
20783  bool CurrentElementFailed = Track->TrackElementAt(1549, CurrentElement).Failed; //added at v2.13.2
20784 
20785  // check if currently stopped at a location, and if so add the remaining dwell time
20786  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
20787  if(TrainID > -1)
20788  // -1 for a continuation and can't be at a location as not yet entered
20789  {
20790  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
20792  // this used to deduct from RecoverableTime when arrive at a location
20793  if(Train.RevisedStoppedAtLoc())
20794  {
20795  if(Train.StoppedForTrainInFront)
20796  {
20797  Utilities->CallLogPop(2082);
20798  return(-1); // no action needed
20799  }
20800  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
20801  {
20802  Utilities->CallLogPop(2083);
20803  return(-1); // not due a departure so no action needed
20804  }
20805  else // due a departure
20806  {
20807  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
20808  // can't convert a TDateTime to a float directly
20809  CurrentStopTime = float(TimeToDepart);
20810  AVPtr++;
20811  }
20812  }
20813  }
20814  // check if CurrentElement is a red signal, but ok if autosig route after provided signal not failed
20815  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
20816  // ok if autosig route after red signal unless signal has failed
20817  {
20818  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
20819  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
20820  int RouteNumber; // holder for referenced value, not used
20821  if((AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !CurrentElementFailed)
20822  { //CurrentElementFailed added at v2.13.2
20823  Utilities->CallLogPop(2078);
20824  return(-1);
20825  }
20826  else if(SigControlAndCanPassRedSignal)
20827  // ignore signal and increment CurrentElement to NextElement
20828  {
20829  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
20830  {
20831  if((NextEntryPos == 0) || (NextEntryPos == 2))
20832  // leading entry point
20833  {
20834  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
20835  {
20836  NextExitPos = 1;
20837  }
20838  else
20839  {
20840  NextExitPos = 3;
20841  }
20842  }
20843  else
20844  {
20845  NextExitPos = 0; // trailing entry point
20846  }
20847  }
20848  else
20849  {
20850  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
20851  }
20852  CurrentElement = NextElement;
20853  CurrentEntryPos = NextEntryPos;
20854  CurrentExitPos = NextExitPos;
20855  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
20856  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
20857  }
20858  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
20859  // give 'NOW' indication after allowed to pass stop signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
20860  {
20861  Utilities->CallLogPop(2084);
20862  return(0);
20863  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
20864  }
20865  }
20866  int LaterStopNumber = 0;
20867  int x = 0;
20868  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
20869 
20870  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
20871  // not red signal next (in fwd direction) so enter loop to calc CumLength
20872  {
20873  x++; // added in v2.4.0 as above
20874  if(x > 5000)
20875  {
20876  Utilities->CallLogPop(2120);
20877  return(-1);
20878  }
20879  if(CurrentEntryPos > 1)
20880  {
20881  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
20882  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
20883  }
20884  else
20885  {
20886  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
20887  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
20888  }
20889  TrackSpeedCount++;
20890 
20891  //added for multiplayer - exiting at a continuation and continuation length already added
20892  if((Track->TrackElementAt(1407, CurrentElement).TrackType == Continuation) && (Track->TrackElementAt(1408, CurrentElement).Config[CurrentExitPos] == End))
20893  {
20894  DistanceToExit = DistanceToRedSignal; //don't need to exit function here as will exit when find that the next Conn value is -1
20895  ExitPair.first = Track->TrackElementAt(1409, CurrentElement).HLoc;
20896  ExitPair.second = Track->TrackElementAt(1410, CurrentElement).VLoc;
20897  //here repeat calcs for MaxAllowableSpeed & AvTrackSpeed as done at end for stop signal
20898  //need here as next element will be -1 so will exit before calcs at end
20899  if(TrackSpeedCount > 0)
20900  {
20901  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
20902  }
20903  else // shouldn't reach here but include to prevent divide by zero error
20904  {
20905  if(CurrentEntryPos > 1)
20906  {
20907  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
20908  }
20909  else
20910  {
20911  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
20912  }
20913  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
20914  }
20915  //calc AvTrackSpeed
20916  if(LaterStopNumber > 0)
20917  {
20918  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
20919  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
20920  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
20921  // average line speed/2 (for half distance accelerating and half decelerating.
20922  }
20923  else
20924  {
20925  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
20926  // using linear trendline for accel & decel distance at various speeds
20927  // at half braking, speed never < 60 using this
20928  }
20929  if(AvTrackSpeed > MaxAllowableSpeed)
20930  {
20931  AvTrackSpeed = MaxAllowableSpeed;
20932  }
20933  }
20934 
20935  // added at v2.6.1 to find DistanceToStationStop for trains running early
20936  if(TrainID > -1) //can ignore continuation entries as these don't run early
20937  {
20938  TTrain &Train = TrainVectorAtIdent(52, TrainID);
20939  if(!Train.StationStopCalculated)
20940  {
20941  if(Train.TrainMode == Timetable)
20942  {
20943  bool StopRequired = false;
20944  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
20945  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
20946  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
20947  {
20948  // no need to add in the length of element to CumulativeLength
20949  if(StopRequired)
20950  {
20951  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
20952  Train.StationStopCalculated = true; //don't want to update it with later stops
20953  }
20954  }
20955  }
20956  }
20957  }
20958  // check for train in front, but if on a bridge on other track then ok
20959  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
20960  int TrainOnElement;
20961  if(TE.TrackType != Bridge)
20962  {
20963  TrainOnElement = TE.TrainIDOnElement;
20964  }
20965  else
20966  {
20967  if(CurrentEntryPos > 1)
20968  {
20970  }
20971  else
20972  {
20974  }
20975  }
20976  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
20977  // train in front before red signal
20978  {
20979  Utilities->CallLogPop(2085);
20980  return(-1);
20981  }
20982  // add to stoptime if required
20983  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
20984  {
20985  double StopTimeDouble;
20986  while(AVPtr->FormatType == PassTime)
20987  {
20988  AVPtr++; // skip past any passes
20989  }
20990  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
20991  (AVPtr->FormatType == TimeTimeLoc)))
20992  // stop due here so calc dwell time & advance Ptr
20993  {
20994  if(AVPtr->FormatType == TimeTimeLoc)
20995  {
20996  LaterStopNumber++;
20997  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
20998  if(StopTimeDouble < 0.5)
20999  {
21000  StopTimeDouble = 0.5;
21001  }
21002  // at least 30 secs delay at station
21003  // can't convert a TDateTime to a float directly
21004  LaterStopTime += float(StopTimeDouble);
21005  RecoverableTime += StopTimeDouble - 0.5;
21006  if((LaterStopNumber == 1) && (TrainID > -1))
21007  {
21008  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
21009  }
21010  AVPtr++;
21011  }
21012  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
21013  {
21014  if((AVPtr + 1)->FormatType == TimeLoc)
21015  // must be a departure
21016  {
21017  LaterStopNumber++;
21018  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
21019  // can't convert a TDateTime to a float directly
21020  if(StopTimeDouble < 0.5)
21021  {
21022  StopTimeDouble = 0.5;
21023  }
21024  // at least 30 secs delay at station
21025  LaterStopTime += float(StopTimeDouble);
21026  RecoverableTime += StopTimeDouble - 0.5;
21027  if((LaterStopNumber == 1) && (TrainID > -1))
21028  {
21029  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
21030  }
21031  AVPtr++;
21032  AVPtr++;
21033  }
21034  else // not a departure, does something else at the location so no calculation needed
21035  {
21036  Utilities->CallLogPop(2086);
21037  return(-1);
21038  }
21039  }
21040  }
21041  }
21042  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
21043  if(NextElement == -1) // reached end element, no action needed
21044  {
21045  Utilities->CallLogPop(2077);
21046  return(-1);
21047  }
21048  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
21049  // get NextExitPos
21050  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
21051  {
21052  if((NextEntryPos == 0) || (NextEntryPos == 2))
21053  // leading entry point
21054  {
21055  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
21056  {
21057  NextExitPos = 1;
21058  }
21059  else
21060  {
21061  NextExitPos = 3;
21062  }
21063  }
21064  else
21065  {
21066  NextExitPos = 0; // trailing entry point
21067  }
21068  }
21069  else
21070  {
21071  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
21072  }
21073  CurrentElement = NextElement;
21074  CurrentEntryPos = NextEntryPos;
21075  CurrentExitPos = NextExitPos;
21076  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
21077  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
21078  CurrentElementFailed = Track->TrackElementAt(1550, CurrentElement).Failed; //added at v2.13.2
21079  }
21080  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
21081  // ok if autosig route after red signal, no action needed
21082  {
21083  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
21084  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
21085  int RouteNumber; // holder for referenced value, not used
21086  if((AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !CurrentElementFailed)
21087  { //CurrentElementFailed added at v2.13.2
21088  Utilities->CallLogPop(2095);
21089  return(-1);
21090  }
21091  }
21092 
21093  if(TrackSpeedCount > 0)
21094  {
21095  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
21096  }
21097  else // shouldn't reach here but include to prevent divide by zero error
21098  {
21099  if(CurrentEntryPos > 1)
21100  {
21101  MaxAllowableSpeed = Track->TrackElementAt(1433, CurrentElement).SpeedLimit23;
21102  }
21103  else
21104  {
21105  MaxAllowableSpeed = Track->TrackElementAt(1434, CurrentElement).SpeedLimit01;
21106  }
21107  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
21108  }
21109 
21110  if(LaterStopNumber > 0)
21111  {
21112  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
21113  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
21114  }
21115  else
21116  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
21117  // average line speed/2 (for half distance accelerating and half decelerating.
21118  {
21119  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
21120  // using linear trendline for accel & decel distance at various speeds
21121  // at half braking, speed never < 60 using this
21122  }
21123  if(AvTrackSpeed > MaxAllowableSpeed)
21124  {
21125  AvTrackSpeed = MaxAllowableSpeed;
21126  }
21127  Utilities->CallLogPop(2096);
21128  return(DistanceToRedSignal);
21129 }
21130 
21131 // ---------------------------------------------------------------------------
21132 // end of TTrainController entries
21133 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:9217
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:18318
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:136
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:378
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:52
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:15281
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:345
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:344
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:487
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:699
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:72
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1728
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:901
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:174
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:15304
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:14949
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller, bool NoLogFlag)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6431
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:366
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2....
Definition: TrainUnit.cpp:20505
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:807
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:16346
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:91
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:53
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:1014
Create
@ Create
Definition: TrainUnit.h:52
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:786
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:19324
Arrive
@ Arrive
Definition: TrainUnit.h:52
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:52
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:343
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:384
Depart
@ Depart
Definition: TrainUnit.h:52
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:718
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:12135
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:716
PerfLogForm
TPerfLogForm * PerfLogForm
Definition: PerfLogUnit.cpp:11
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:428
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:801
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:285
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7305
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1645
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:53
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:907
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1657
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:727
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:142
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:783
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1669
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:66
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:66
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:504
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:747
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:67
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:432
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:71
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:20073
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:14227
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:152
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:366
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:1000
TTrain::TrainFailed
bool TrainFailed
Definition: TrainUnit.h:412
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:983
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:793
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:339
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:979
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:19797
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:8093
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:386
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:478
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:366
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6554
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:793
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:10500
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:321
THVShortPair
std::pair< short, short > THVShortPair
Definition: InterfaceUnit.h:81
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:898
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route
Definition: TrainUnit.h:721
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:16077
TExitInfo::TimeToExitSecs
short TimeToExitSecs
Definition: TrainUnit.h:110
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:851
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:1006
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:826
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:1013
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:184
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:19114
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:793
TTrainController::ControllerGetNewServiceDepartureInfo
AnsiString ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::GetNewServiceDepartureInfo for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:10691
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:12088
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1649
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:9189
TNumListIterator
TNumList::iterator TNumListIterator
Definition: TrainUnit.h:97
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:136
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:966
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:743
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:16092
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:265
Intermediate
@ Intermediate
Definition: TrainUnit.h:77
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:964
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6354
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:980
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3593
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:681
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:66
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:500
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:460
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:10462
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:54
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:493
NotStarted
@ NotStarted
Definition: TrainUnit.h:88
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10882
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:845
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:789
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:346
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:721
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:372
TTrainDataEntry
Contains all data for a single timetable service entry.
Definition: TrainUnit.h:207
LeadMid
@ LeadMid
Definition: TrainUnit.h:298
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:835
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:19588
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:15029
TTrack::ResetAllTrainIDsAndFailedPointOrigSpeedLimits
void ResetAllTrainIDsAndFailedPointOrigSpeedLimits(int Caller)
Definition: TrackUnit.cpp:7677
TTrainController::RebuildTimeToExitMultiMap
void RebuildTimeToExitMultiMap(int Caller)
new for multiplayer
Definition: TrainUnit.cpp:20676
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:978
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6575
TOneRoute::RouteID
int RouteID
the ID number of the route, this is needed for session saves
Definition: TrackUnit.h:1547
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:739
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:1020
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:252
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:1016
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:215
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:855
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:812
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:729
TTrain::SkippedDeparture
bool SkippedDeparture
< used for terminating a service early and becoming new follow-on service
Definition: TrainUnit.h:327
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:9666
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:16195
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:298
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:221
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:892
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:16133
StartNew
@ StartNew
Definition: TrainUnit.h:66
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:52
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:298
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:805
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:347
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:53
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:448
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:729
TTrainController::TimeToExitMultiMap
TTimeToExitMultiMap TimeToExitMultiMap
Map of times to exit & exit coordinates.
Definition: TrainUnit.h:995
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:18862
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:11696
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:989
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5860
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:507
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:186
TTrain
Definition: TrainUnit.h:304
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:986
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:11715
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11618
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:887
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:250
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:869
TPerfLogForm::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: PerfLogUnit.cpp:32
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:484
GapJump
@ GapJump
Definition: TrackUnit.h:65
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:1007
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:838
NoSequence
@ NoSequence
Definition: TrainUnit.h:77
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:150
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:150
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:408
Finish
@ Finish
Definition: TrainUnit.h:77
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6305
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:10093
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:366
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1696
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:366
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:360
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:225
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:702
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:364
TExitInfo::TExitInfo
TExitInfo()
Definition: TrainUnit.cpp:63
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:16174
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:831
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:323
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:174
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:784
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:464
TExitInfo::RepeatNumber
short RepeatNumber
Definition: TrainUnit.h:109
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:717
TTrain::LastSigPassedFailed
bool LastSigPassedFailed
flag used to erase route elements in an autosigs route after a failed signal
Definition: TrainUnit.h:394
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:15897
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1407
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:6131
End
@ End
Definition: TrackUnit.h:75
TUtilities::FixedMinRepairTime
int FixedMinRepairTime
Definition: Utilities.h:66
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:398
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:7173
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8854
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:341
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10607
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:67
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:303
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:404
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:16257
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:124
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:974
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:788
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:1024
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:843
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:209
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:832
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:422
SignalPost
@ SignalPost
Definition: TrackUnit.h:65
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:968
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled event, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:468
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:143
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1673
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:349
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:12776
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:996
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:209
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5633
TUtilities::MinorDelayFactor
float MinorDelayFactor
Definition: Utilities.h:47
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:150
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:846
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:770
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:896
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:420
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:74
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:480
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:64
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:841
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:807
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:895
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1887
TExitInfo
Definition: TrainUnit.h:106
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TUtilities::ModerateDelayCutoff
float ModerateDelayCutoff
Definition: Utilities.h:45
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:484
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:839
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:984
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:66
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:93
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:136
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:484
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:17054
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:962
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:458
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:987
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:11033
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:741
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:366
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:456
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:81
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:1012
Pass
@ Pass
Definition: TrainUnit.h:54
TTrain::SkipPtrValue
int SkipPtrValue
stores the pointer increment from first action in ActionVector for skipped actions when a departure i...
Definition: TrainUnit.h:374
TNumList
std::list< int > TNumList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:93
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:793
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:755
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:72
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:3223
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11330
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:823
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:69
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:999
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:491
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:773
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:877
TUtilities::ModerateDelayFactor
float ModerateDelayFactor
Definition: Utilities.h:48
TUtilities::MajorDelayCutoff
float MajorDelayCutoff
Definition: Utilities.h:46
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:972
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:715
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:354
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:317
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:1005
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:818
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:9201
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:1008
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:211
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:66
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:221
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:7025
TTrainController::MTBFHours
double MTBFHours
Mean time between train failures in timetable clock hours.
Definition: TrainUnit.h:810
TTrack::TInfrastructureFailureEntry::TVPos
int TVPos
Definition: TrackUnit.h:712
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:17947
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:472
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:15566
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10748
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:807
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:311
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3431
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:1019
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:466
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:5223
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2744
RearSplit
@ RearSplit
Definition: TrainUnit.h:52
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:134
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:54
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:825
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:665
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:969
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:816
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:992
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:134
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:55
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10642
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:3155
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6997
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:15664
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:54
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:891
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:14327
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:164
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:837
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:861
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:380
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3543
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:965
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:9265
TUtilities::SignalChangeEventsPerFailure
int SignalChangeEventsPerFailure
number of signal changes between failures - reciprocal of failure probability per change
Definition: Utilities.h:89
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:331
TTrack::TInfrastructureFailureEntry
Definition: TrackUnit.h:711
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:9011
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:8290
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:3147
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:822
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:487
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:462
NewService
@ NewService
Definition: TrainUnit.h:52
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:16410
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1509
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:484
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:382
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1656
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:370
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:223
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:219
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:829
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TUtilities::CumulativeDelayedRandMinsAllTrains
int CumulativeDelayedRandMinsAllTrains
the running total of all random delays including knock-on delays for all trains, used to reduce total...
Definition: Utilities.h:95
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:169
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:1009
Crossover
@ Crossover
Definition: TrackUnit.h:65
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4823
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:735
AtLocation
@ AtLocation
Definition: TrainUnit.h:72
Signal
@ Signal
Definition: TrackUnit.h:75
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:971
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:1002
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:793
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:18555
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:875
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:19437
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1657
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:464
Exited
@ Exited
Definition: TrainUnit.h:88
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10593
TTrain::ActionsSkippedFlag
bool ActionsSkippedFlag
prevents any further skipping until after the next departure
Definition: TrainUnit.h:329
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:16108
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:8577
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:18381
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:1021
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2362
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:484
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:59
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1678
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:961
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:484
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:753
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:313
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:795
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:52
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:430
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:807
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:991
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:967
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:366
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:66
TTrain::NewDelay
double NewDelay
an additional random delay at a location (added at v2.13.0)
Definition: TrainUnit.h:440
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:1003
Leave
@ Leave
Definition: TrainUnit.h:52
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:848
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:765
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:791
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel (OperatorA...
Definition: TrainUnit.h:454
TTrack::FailedSignalsVector
TFailedElementVector FailedSignalsVector
Definition: TrackUnit.h:791
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:396
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:3128
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:970
NoFormat
@ NoFormat
Definition: TrainUnit.h:66
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:803
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:66
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2673
TTrain::TimeToExit
float TimeToExit
in minutes: new for multiplayer, -1 = > 60 mins
Definition: TrainUnit.h:452
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:484
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3572
Enter
@ Enter
Definition: TrainUnit.h:52
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:515
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7892
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:977
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:16157
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:446
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:276
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:14857
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:745
TTrain::NewTrainService
void NewTrainService(int Caller, bool NoLogFlag)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6510
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:9701
Timetable
@ Timetable
Definition: TrainUnit.h:60
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:15236
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:7082
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:801
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:11181
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:783
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:132
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:18225
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:109
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:793
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:273
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:20397
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:82
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:436
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:763
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:263
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:799
Terminate
@ Terminate
Definition: TrainUnit.h:52
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:807
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
< map of coupled continuations
Definition: TrackUnit.h:797
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:484
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:148
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:498
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:368
Running
@ Running
Definition: TrainUnit.h:88
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:285
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3669
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:896
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:14199
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:15676
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:16433
Nil
@ Nil
Definition: Utilities.h:37
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:456
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11401
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:725
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:239
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:849
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:424
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2559
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:19239
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0 for train failures, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:865
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1643
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:10182
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:9754
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6924
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:859
TTrain::GetNewServiceDepartureInfo
AnsiString GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr, bool TimetableTime)
called during FloatingLabelNextString to find the next service departure time & next location (last b...
Definition: TrainUnit.cpp:7693
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:994
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:127
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:517
TUtilities::DelayMode
TDelayMode DelayMode
specifies whether no delays or minor, moderate or major random delays are to be applied (added at v2....
Definition: Utilities.h:111
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:9765
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:1011
TTrain::ExitPair
THVShortPair ExitPair
H & V coordinates of the exit element related to TimeToExit, new for multiplayer.
Definition: TrainUnit.h:470
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1730
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:807
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:12176
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3627
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3327
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:464
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
new v2.2.0 (DistanceToExit added for multiplayer), calcs distances to red signal & exit,...
Definition: TrainUnit.cpp:20710
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:805
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:72
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:54
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1641
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:9158
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:3191
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:122
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:825
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:16279
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:266
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:988
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:15643
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:146
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:867
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:485
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4760
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:278
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in Actions...
Definition: TrainUnit.cpp:10516
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:53
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:356
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:406
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:12276
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:821
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:96
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:9779
TTrackElement::Failed
bool Failed
New parameter added at v2.13.0 for failed points, signals & TSRs.
Definition: TrackUnit.h:140
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:154
TTrainController::SkippedTTEvents
int SkippedTTEvents
Definition: TrainUnit.h:844
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:903
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:444
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:747
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:159
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1711
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:213
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:129
TUtilities::MinorDelayCutoff
float MinorDelayCutoff
Definition: Utilities.h:44
TTimeToExitMultiMapEntry
std::pair< THVShortPair, TExitInfo > TTimeToExitMultiMapEntry
Definition: TrainUnit.h:116
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:993
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:227
TTrain::DelayedRandMins
double DelayedRandMins
the remaining random delay at any point in time for the train (added at v2.13.0)
Definition: TrainUnit.h:438
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:148
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:814
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:907
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:558
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:5246
TTrain::TRSTime
TDateTime TRSTime
Definition: TrainUnit.h:466
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1647
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:101
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1657
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:140
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:335
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:1017
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:708
Moderate
@ Moderate
Definition: Utilities.h:37
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:706
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:10166
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:894
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:830
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:352
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:16446
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:767
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:982
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:152
Major
@ Major
Definition: Utilities.h:37
Points
@ Points
Definition: TrackUnit.h:65
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:11768
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:740
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:392
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:824
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:14756
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:390
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:807
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:847
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:54
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3517
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:1001
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:400
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:502
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all signals appropriately. Also called when a new train is added a...
Definition: TrackUnit.cpp:17332
Continuation
@ Continuation
Definition: TrackUnit.h:65
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:793
TTrain::ActualArrivalTime
TDateTime ActualArrivalTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:466
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7650
GraphicUnit.h
PerfLogUnit.h
TUtilities::MajorDelayFactor
float MajorDelayFactor
Definition: Utilities.h:49
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:765
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:840
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:476
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:10905
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:55
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:362
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:19905
TTrack::TInfrastructureFailureEntry::FailureTime
TDateTime FailureTime
Definition: TrackUnit.h:713
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3285
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:899
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:54
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:776
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:902
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:1015
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:10547
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:87
TTrain::FollowOnServiceRef
AnsiString FollowOnServiceRef
Definition: TrainUnit.h:325
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:482
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:675
Minor
@ Minor
Definition: Utilities.h:37
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:739
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6757
TActionVectorEntry::ExitList
TNumList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:138
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:842
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:489
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:267
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:179
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:509
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:54
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:998
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:19903
TTrain::RevisedStoppedAtLoc
bool RevisedStoppedAtLoc() const
Definition: TrainUnit.h:515
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrainController::GetServiceFromVector
TTrainDataEntry GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
Definition: TrainUnit.cpp:19159
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:901
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:990
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:11039
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:315
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:285
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:707
TTrainController::SingleServiceOutput
void SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
Outputs the single service vector for train direction analysis purposes in timetable conflict analysi...
Definition: TrainUnit.cpp:19052
TTrack::TInfrastructureFailureEntry::RepairTime
TDateTime RepairTime
Definition: TrackUnit.h:714
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:717
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:9121
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:65
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:793
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:320
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:762
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:187
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:723
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:985
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:19012
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:18968
TTrain::CumulativeDelayedRandMinsOneTrain
double CumulativeDelayedRandMinsOneTrain
the running total of all random delays including knock-on delays for a single train,...
Definition: TrainUnit.h:442
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:797
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:124
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:282
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
Definition: TrackUnit.h:154
Start
@ Start
Definition: TrainUnit.h:77
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:906
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:333
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:51
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:717
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:366
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:834
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:1022
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:82
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:10133
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:15590
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:7865
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:18238
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6814
TTrain::AValue
double AValue
< only true when a train has become a follow-on service early and the follow-on service normally pass...
Definition: TrainUnit.h:416
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5686
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:19195
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:10151
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:148
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:14206
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:342
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:16239
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:185
TUtilities::MaxRandomRepairTime
int MaxRandomRepairTime
Definition: Utilities.h:65
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:154
NoLocation
@ NoLocation
Definition: TrainUnit.h:72
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:933
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:388
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:217
NoMode
@ NoMode
Definition: TrainUnit.h:60
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:836
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:12624
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:99
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:766
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:807
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:66
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:176
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:199
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:857
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:828
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:805
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:522
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5330
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:484
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:10535
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:981
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:142
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:297
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:44
TTrain::TrainSkippedEvents
int TrainSkippedEvents
stores the pointer increment from the current action in ActionVector for skipped actions when a depar...
Definition: TrainUnit.h:376
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:778
TExitInfo::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:108
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable - one entry per timetable service entry (the object i...
Definition: TrainUnit.h:240
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:15270
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7389
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:450
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:704
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1651
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:144
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:358
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:768
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:975
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:1018
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:764
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:3249
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled event entries, null on creation
Definition: TrainUnit.h:124
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:146
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:410
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:99
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:1010
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:124
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6859
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4955
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:737
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:150
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:402
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:853
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:807
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:973
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6413
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:725
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:66
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:53
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:767
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:20351
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:733
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:66
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:820
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:1004
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:871
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:719
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:128
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:5235
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:434
TUtilities::LastDelayTTClockTime
double LastDelayTTClockTime
Clock time at which the latest delay for any train occurred. Used to prevent new delays within 5 minu...
Definition: Utilities.h:79
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:150
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:723
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:337
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:753
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:729
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:67
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:833
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:3173
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:89
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:650
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1071
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:995
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:19479
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6785
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
Definition: TrackUnit.h:154
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3669
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:130
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2761
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:189
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:209
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:7113
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:340
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:75
Repeat
@ Repeat
Definition: TrainUnit.h:67
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:963
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:14215
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:675
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:69
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:807
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:160
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:66
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:149
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:485
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:997
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:747
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:882
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:248
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5996
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:19221
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:144
Signaller
@ Signaller
Definition: TrainUnit.h:60
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:495
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:904
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:183
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:892
TTrain::TreatPassAsTimeLocDeparture
bool TreatPassAsTimeLocDeparture
< indicates failure
Definition: TrainUnit.h:414
Bridge
@ Bridge
Definition: TrackUnit.h:65
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:976
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:280
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:366
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:76
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:426
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:77
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:525
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:65
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:134
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:124